.NET C# Programming

How to call C# (WebAssembly) with JavaScript in ASP.NET MVC

Pinterest LinkedIn Tumblr

In this article, we explore how we can execute C# code using JavaScript within a ASP.NET MVC Core Application. This approach allows us to reuse our existing C# code and libraries without the need to rewrite them in JavaScript or other client-side languages for front-end usage. Additionally, we’ll make use of the Blazor WebAssembly project to convert our existing C# code to WebAssembly automatically and use it in JavaScript.

The code is available on GitHub

Approaching the problem

One option would involve converting our code to JavaScript, but this presents maintainability issues because it leads to code duplication. Another approach is to call the backend with all necessary parameters, execute the computation, and then return the result to the client. However, the increased network traffic, server CPU load, and the constant live connection required by the client make this approach in many cases less appealing.

By using Blazor WebAssembly, we can reuse the same C# code that is written in the backend in the frontend as well.

Overall solution Structure

Out solution consists of an ASP.NET MVC project, a Blazor WebAssembly project, and a C# library. The primary objective is to utilize the C# library via JavaScript in the frontend of the MVC project.

Creating the C# Library

To start, we’ll create our project by selecting a C# library project:

Next, Visual Studio displays a window to configure our new project. For the Project name, input WASM.Lib, and for the solution name, something more generic like Project.WASM. Click on Create to proceed.

You can check the .NET version used (e.g. .NET7) in the csproj file of the WASM.Lib project. You can modify this by double-clicking the project in the solution explorer of Visual Studio or by directly opening the .csproj file of the project.

<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
      <TargetFramework>net7.0</TargetFramework>
   </PropertyGroup>
</Project>

Within WASM.Lib project, create a class named Calculator. Inside this class, we will add the logic we intent to execute via WebAssembly in the MVC application.

using System;
namespace WASM.Lib
{
   public class Calculator
   {
      public int Calculate(int x, int y)
      {
         return x + y;
      }
   }
}

Creating the Blazor WebAssembly Project

Within the same solution, create a new Blazor WebAssembly project:

This WebAssembly project will have a reference to the C# library we intent to convert to WebAssembly and call it via JavaScript. The csproj file of the Blazor WebAssembly project should look like the following:

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
	  <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.9" />
	  <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.9" PrivateAssets="all" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\WASM.Lib\WASM.Lib.csproj" />
  </ItemGroup>

</Project>

Next, we leave the Program.cs as is:

public class Program
{
   public static async Task Main(string[] args)
   {
      var builder = WebAssemblyHostBuilder.CreateDefault(args);
      builder.RootComponents.Add<App>("#app");
      builder.Services.AddScoped(sp => 
         new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)});   
      builder.Services.AddTransient<Calculator>();   
      await builder.Build().RunAsync();
   }
}

Additionally, create a razor component under Pages and name it CalculatorWASMComponent.razor. This component will reference a Calculator object and call the Calculate method with two different approaches, one with an instance method and another with a static one.

@page "/calculator-wasm"
@using WASM.Lib; // Add reference to the C# library.
@inject IJSRuntime jsRuntime
@inject Calculator calculator

@code{

    protected override void OnInitialized()
    {
        // Pass the .NET instance to JavaScript to invoke an instance
        // C# method on the client side.
        jsRuntime.InvokeVoidAsync("calculator_wasm.init", DotNetObjectReference.Create(this));
        // The calculator_wasm.init is a JavaScript method located in the index.html
        // of the project.
    }

    [JSInvokable("CalculateStatic")]
    public static string CalculateStatic(int x, int y)
    {
        try
        {
            Console.WriteLine("called");
            var res = new Calculator().Calculate(x, y);
            Console.WriteLine("executed");
            return res.ToString();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
            return null;
        }
    }

    [JSInvokable("Calculate")]
    public string Calculate(int x, int y)
    {
        try
        {
            Console.WriteLine("called");
            var res = calculator.Calculate(x, y);
            Console.WriteLine("executed");
            return res.ToString();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
            return null;
        }
    }
}

As we can see from the previous razor component, we can implement dependency injection using the @inject attribute. This way, we inject our Calculator service into the component. To call the Calculate method of the component from JavaScript, we decorate it with the JSInvokable attribute and give it a name. Additionally, to invoke this method from JavaScript at the instance level (and not as static), we use the OnInitialized method and provide the jsRuntime with the instance of the component.

Invoking a Static C# Method using JavaScript

To call the static method CalculateStatic we created in the CalculatorWASMComponent.razor, we can use the DotNet.invokeMethodAsync method provided by the blazor.webassembly.js file. This method has the following signature:

DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

In our case, by running the Blazor WebAssembly project, we can call the static CalculateStatic method by providing the appropriate parameters and obtaining the result as shown below:

DotNet.invokeMethodAsync('WASM.Blazor', 'CalculateStatic', 1, 1)
      .then(data => {
        console.log(data); // Output: 2
      });

Invoking an Instance C# Method using JavaScript

To call an instance .NET method, we pass the .NET instance by reference to JavaScript using the DotNetObjectReference create method. Then, we can call the desired method by using the passed object in JavaScript and invoke the invokeMehtodAsync with the proper parameters to execute our method.

The steps are shown below:

First, pass the .NET reference to JavaScript.

@inject IJSRuntime jsRuntime
@code{
   protected override void OnInitialized()
   {
      jsRuntime.InvokeVoidAsync("calculator_wasm.init", DotNetObjectReference.Create(this));
   }
}

The calculator_wasm.init is a Javascript function that stores the .NET instance. To invoke a JavaScript method, we use the jsRuntime.InvokeVoidAsync method. The JavaScript method that stores the .NET reference is shown below:

var dotnet_ref;
window.calculator_wasm = {
   init: dotnetHelper => {
      dotnet_ref = dotnetHelper;
   }
};

Now, we can call the Calculate instance method as follows:

dotnet_ref.invokeMethodAsync('Calculate', 1, 1)
   .then(res => {
      console.log(res); // Output: 2
   });

The final index.html of the Blazor WebAssembly project is presented below:

<body>
   <div id="app">Loading...</div>
   <script>
      var dotnet_ref;
      window.calculator_wasm = {
         init: dotnetHelper => {
            dotnet_ref = dotnetHelper;
         }
      };
   function Calculate(x, y, resultCallbackFunc) {
      console.log("calling dotnet.");
      dotnet_ref.invokeMethodAsync('Calculate', x, y)
         .then(res => {                 
            resultCallbackFunc(res);             
      });     
   }
      
   <script>
   <script src="_framework/blazor.webassembly.js"></script>
</body>

Because we will encapsulate the Blazor component in an iframe in the MVC app, we need a way to call the Calculate method from the MVC app. Additionally, we can provide a callback function to retrieve the result. The Calculate function serves this purpose.

ASP.NET MVC Application

In the solution, create a new ASP.NET Core Web Application project:

Name the project as WASM.Web and hit Create.

The first step is to add a project reference to our Blazor Project. Then, within the Startup class, inside the Configure method, we need to include a few lines of configuration related to Blazor:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
   if (env.IsDevelopment())
   {
      app.UseDeveloperExceptionPage();
   }
   else
   {
      app.UseExceptionHandler("/Home/Error");
   }

   app.UseBlazorFrameworkFiles();

   app.UseStaticFiles();
   app.UseRouting();
   app.UseAuthorization();
   app.UseEndpoints(endpoints =>
   {
      endpoints.MapControllerRoute(
         name: "default",
         pattern: "{controller=Home}/{action=Index}/{id?}");

      endpoints.MapFallbackToFile("index.html");
   });
}

Invoking C# Code in Client Side

Next, in the Views/Home/Index.cshtml file, we will add an iframe to call the CalculatorWASMComponent. This will also download all the WebAssembly files of the Calculator service. Additionally, we will include two input fields and a button to execute the calculate function. JavaScript will be added to retrieve the input values and call the Calculator function from the iframe.

<div class="text-center">
   <h1 class="display-4">Welcome To WebAssembly Demo</h1>
   <iframe style="visibility:collapse; display:contents" id="calculatorIFrame" src="/calculator-wasm"></iframe>
   <input type="number" id="input1">
   <input type="number" id="input1">

   <button onclick="calculate()">calculate</button>
</div>

<script>
   function calculate() {
      let x = parseInt($("#input1").val());     
      let y = parseInt($("#input2").val());     
      document.getElementById('calculatorIFrame').
      contentWindow.Calculate(x, y, InterpreterResul    tReceived); 
   } 
   function InterpreterResultReceived(res) {     
      alert("Result from C# is: " + res); 
   }
</script>

Usage Example:

After clicking the calculate button, we receive the result in an alert window as shown below:

The code is available on GitHub

Write A Comment

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.