Aspnetcore.docs: Unmarshalled JavaScript Isolation Code sample is incorrect

Created on 15 Nov 2020  ·  7Comments  ·  Source: dotnet/AspNetCore.Docs

Unmarshalled JavaScript Isolation Code sample is incorrect

var unmarshalledRuntime = (IJSUnmarshalledRuntime)js;
var jsUnmarshalledReference = unmarshalledRuntime
    .InvokeUnmarshalled<IJSUnmarshalledObjectReference>("unmarshalledInstance");

string helloWorldString = jsUnmarshalledReference.InvokeUnmarshalled<string, string>(
    "helloWorld");

Error

There is no argument given that corresponds to the required formal parameter 'arg0' of 'IJSUnmarshalledObjectReference.InvokeUnmarshalled<T0, TResult>(string, T0)'

incorrect line 👇

string helloWorldString = jsUnmarshalledReference.InvokeUnmarshalled<string, string>(
    "helloWorld");

Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Blazor P0 PU Source - Docs.ms doc-bug

Most helpful comment

@pranavkm ... I was playing around with this, and what I found in the framework tests here and here with this works. Could/should the example be more like that (example below :point_down:), or can you fix the example that we're showing? I couldn't make the existing example work on a first pass.

Whichever way the example goes, I have a few questions on unmarshalled interop coverage ...

  1. Do we need to say that int[] and string between .NET and JS don't match and can't be passed back (?) without additional work per https://github.com/dotnet/aspnetcore/issues/21492#issuecomment-637201926. I say "back" because when I did a test trying to pass back a string, it didn't break but didn't work either. Passing back a bool or an int worked fine.
  2. Do we need to call out that this scenario really isn't relevant for these types of examples ... that the perf difference is trivial for small bits of data and that this scenario would shine when moving large, complex objects? Should the example be more along those lines (i.e., create a fake, large, complex object with random data and interop with that as opposed to a string or a string and a number)?
  3. The framework example has a JS dispose. Should that be included, as I have below?
  4. Don't we need to call out that this scenario is strictly for WASM?
  5. Given that this is an advanced scenario for perf-critical scenarios, it seems like it should be _in its own section_. Should I add a section heading right above where this shows up in the doc?

Here's an idea based on the framework tests ...

Inside the closing <body> tag of wwwroot/index.html ...

<script>
  function returnJSObjectReference() {
    return {
      dispose: function () {
        DotNet.disposeJSObjectReference(this);
      },
      unmarshalledFunction: function (fields) {
        const name = Blazor.platform.readStringField(fields, 0);
        const year = Blazor.platform.readInt32Field(fields, 8);

        return name === "Brigadier Alistair Gordon Lethbridge-Stewart" && 
          year === 1968;
      }
    };
  }
</script>

Pages/Hello.razor ...

@page "/hello"
@using System.Runtime.InteropServices
@using Microsoft.JSInterop
@inject IJSRuntime JS

<h1>Say Hello</h1>

<p>
    @message
</p>

<p>
    <button @onclick="SayHello">Say Hello</button>
</p>

<p>
    <a href="https://www.doctorwho.tv">Doctor Who</a> 
    is a registered trademark of the <a href="https://www.bbc.com/">BBC</a>.
</p>

@code {
    private string message;

    private void SayHello()
    {
        var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;

        var jsUnmarshalledReference = unmarshalledRuntime
            .InvokeUnmarshalled<IJSUnmarshalledObjectReference>(
                "returnJSObjectReference");

        message = jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, bool>(
            "unmarshalledFunction",
            new InteropStruct
            {
                Name = "Brigadier Alistair Gordon Lethbridge-Stewart",
                Year = 1968,
            }).ToString();
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct InteropStruct
    {
        [FieldOffset(0)]
        public string Name;

        [FieldOffset(8)]
        public int Year;
    }
}

All 7 comments

Thanks @sps014 ... This was one of the examples that came in directly from the product unit. Let me take a look and get back to you either later today or on Monday morning.

@pranavkm ... I was playing around with this, and what I found in the framework tests here and here with this works. Could/should the example be more like that (example below :point_down:), or can you fix the example that we're showing? I couldn't make the existing example work on a first pass.

Whichever way the example goes, I have a few questions on unmarshalled interop coverage ...

  1. Do we need to say that int[] and string between .NET and JS don't match and can't be passed back (?) without additional work per https://github.com/dotnet/aspnetcore/issues/21492#issuecomment-637201926. I say "back" because when I did a test trying to pass back a string, it didn't break but didn't work either. Passing back a bool or an int worked fine.
  2. Do we need to call out that this scenario really isn't relevant for these types of examples ... that the perf difference is trivial for small bits of data and that this scenario would shine when moving large, complex objects? Should the example be more along those lines (i.e., create a fake, large, complex object with random data and interop with that as opposed to a string or a string and a number)?
  3. The framework example has a JS dispose. Should that be included, as I have below?
  4. Don't we need to call out that this scenario is strictly for WASM?
  5. Given that this is an advanced scenario for perf-critical scenarios, it seems like it should be _in its own section_. Should I add a section heading right above where this shows up in the doc?

Here's an idea based on the framework tests ...

Inside the closing <body> tag of wwwroot/index.html ...

<script>
  function returnJSObjectReference() {
    return {
      dispose: function () {
        DotNet.disposeJSObjectReference(this);
      },
      unmarshalledFunction: function (fields) {
        const name = Blazor.platform.readStringField(fields, 0);
        const year = Blazor.platform.readInt32Field(fields, 8);

        return name === "Brigadier Alistair Gordon Lethbridge-Stewart" && 
          year === 1968;
      }
    };
  }
</script>

Pages/Hello.razor ...

@page "/hello"
@using System.Runtime.InteropServices
@using Microsoft.JSInterop
@inject IJSRuntime JS

<h1>Say Hello</h1>

<p>
    @message
</p>

<p>
    <button @onclick="SayHello">Say Hello</button>
</p>

<p>
    <a href="https://www.doctorwho.tv">Doctor Who</a> 
    is a registered trademark of the <a href="https://www.bbc.com/">BBC</a>.
</p>

@code {
    private string message;

    private void SayHello()
    {
        var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;

        var jsUnmarshalledReference = unmarshalledRuntime
            .InvokeUnmarshalled<IJSUnmarshalledObjectReference>(
                "returnJSObjectReference");

        message = jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, bool>(
            "unmarshalledFunction",
            new InteropStruct
            {
                Name = "Brigadier Alistair Gordon Lethbridge-Stewart",
                Year = 1968,
            }).ToString();
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct InteropStruct
    {
        [FieldOffset(0)]
        public string Name;

        [FieldOffset(8)]
        public int Year;
    }
}

Thanks @guardrex

I modified so that I can easily import JS Lib , we no longer need to reference in index.html.
Most people would be happy if they do no require to reference js in index.html

Is this a the ideal way code to import ?

window.returnJSObjectReference = () =>
{
    return {
        dispose: function () {
            DotNet.disposeJSObjectReference(this);
        },
        unmarshalledFunction: function (fields) {
            const name = Blazor.platform.readStringField(fields, 0);
            const year = Blazor.platform.readInt32Field(fields, 8);

            return name === "Brigadier Alistair Gordon Lethbridge-Stewart" &&
                year === 1968;
        }
    };
}
@page "/"

@using System.Runtime.InteropServices
@using Microsoft.JSInterop
@inject IJSRuntime JS



md5-495c3261f2ae8b705494ad5312125307





md5-e2cc5d4bf3cc75cb37b8992f6b1d272c





md5-2201867fbf306939021ca997df5474ac





md5-f5e64cbc6b2b5d1e77f0e8ffdf30833d



@code {
    private  string message;

    protected override async Task OnInitializedAsync()
    {
        await JS.InvokeVoidAsync("import", "./my.js");
    }

    private async void SayHello()
    {
        System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();

        var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;

        var jsUnmarshalledReference = unmarshalledRuntime
            .InvokeUnmarshalled<IJSUnmarshalledObjectReference>(
                "returnJSObjectReference");
        stopwatch.Start();

        message = jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, bool>(
    "unmarshalledFunction",
    new InteropStruct
    {
        Name = "Brigadier Alistair Gordon Lethbridge-Stewart",
        Year = 1968,
    }).ToString();
        stopwatch.Stop();
        Console.WriteLine(stopwatch.ElapsedMilliseconds);
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct InteropStruct
    {
        [FieldOffset(0)]
        public string Name;

        [FieldOffset(8)]
        public int Year;
    }
}

Most people would be happy if they do no require to reference js in index.html

I was just taking the simplest approach to demonstrate the _Blazor_ concept.

ideal way code to import

We have a little bit of coverage at, which I think you saw there above the unmarshalled interop coverage ...

https://docs.microsoft.com/aspnet/core/blazor/call-javascript-from-dotnet?view=aspnetcore-5.0#blazor-javascript-isolation-and-object-references

... and I'll leave that to Pranav to remark further.

thanks once again @guardrex

Ping @pranavkm ... Sorry for the double-ping (or perhaps ur OOF right now). This is a live topic doc bug 🐞. I can fix it, but it's best if we discuss it first ... https://github.com/dotnet/AspNetCore.Docs/issues/20598#issuecomment-727663432. I can ask Safia to help if ur busy/out.

Was this page helpful?
0 / 5 - 0 ratings