Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loading an external .NET Standard 2.0 assembly with blazor

In a Blazor app, I want to load an external assembly and execute a method. For this, I have created a new ASP.Net Core webapplication using the Blazor template.

Then, in a Razor Page (which will be compiled and executed by browser/wasm) I use reflection to load the assembly and run the method (based on code found here)

// download external assembly from server
HttpClient client = new HttpClient();
var bytes = await client.GetByteArrayAsync("http://localhost:62633/_framework/MyCustomLib.dll");

//load assembly
var assembly = System.Reflection.Assembly.Load(bytes);

// get type/method info
var type = assembly.GetType("MyCustomLib.MyCustomClass");
var method = type.GetMethod("WriteSomething");

// instantiate object and run method
object classInstance = Activator.CreateInstance(type, null);
method.Invoke(classInstance, null);

The method WriteSomething contains a single Console.WriteLine() which prints something in the browser's console, thanks to blazor/mono.wasm goodness. The complete code in this library is:

namespace MyCustomLib
{
    public class MyCustomClass
    {
        public void WriteSomething()
        {
            System.Console.WriteLine("This is printed from a loaded dll 3 !");
        }
    }
}

Result:

Successful result in chrome's console

As you can see, this works great when MyCustomLib.dll is built as a .NET Framework Class Library. However, I want to use a .NET Standard Class Library.

When I build a MyCustomLib.dll as a .NET Standard 2.0 library, and execute the same blazor app, I get the following error in the browser's console:

Could not load file or assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies.

I would expect mono.wasm would have loaded the necessary dependencies to support .NET Standard assemblies.

  • Loading the assembly into the AppDomain yields the same result.
var assembly = AppDomain.CurrentDomain.Load(bytes); 
  • Switching down to netstandard 1.6 gives me a similar error, this time about System.Runtime (because mono.wasm expects Mono.Runtime I assume).

  • Maybe there's a way to perform LoadAssembly on the assemblies referenced by the netstandard2.0 package, but I wouldn't know how.

How can I load a .NET Standard 2.0 into the browser environment using Blazor?

like image 831
Sigge Avatar asked Jan 20 '19 01:01

Sigge


People also ask

Does Blazor work with .NET framework?

Blazor WebAssembly is a single-page app (SPA) framework for building interactive client-side web apps with . NET. Blazor WebAssembly uses open web standards without plugins or recompiling code into other languages. Blazor WebAssembly works in all modern web browsers, including mobile browsers.

Why is Blazor not popular?

Blazor projects are slow on the client-side because you have to download the entire dot net runtime along with the necessary DLL libraries on your browser. Additionally, Blazor apps have latency issues.

Can Blazor access local files?

Blazor code can now browse local file systems and open up local files and edit them in native Windows apps. With . NET 6 and the latest Visual Studio 2022 preview, developers can create a hybrid Blazor/.


2 Answers

This is caused by the blazor linker stripping out the dependency to netstandard since it's not explicitly used in the main WebAssemblyHost project.

When dynamically loading a DLL that uses netstandard, it will assume that it was already loaded and crash.

If you plan to load assemblies dynamically, I recommend using

<BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>

to ensure non of the base dependencies is stripped out.

like image 145
Ivan Sanz Carasa Avatar answered Oct 13 '22 00:10

Ivan Sanz Carasa


After doing some further investigation, I've concluded that my problem is that my external library is not properly linked to the mono.net dependencies. This is why, when you build a Blazor app, it is compiled a second time to /dist/_framework/_bin.

I've found three possible solutions to this problem:

1. Turn the external class library into a Blazor Web app

This way, your app will automatically be converted to a mono-compatible assembly when built. A simple peek in to a Blazor .csproj shows the dependencies needed to achieve this. For it to work, I had to change the .csproj of my external assembly:

from a default netstandard library:

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

into a web app:

<Project Sdk="Microsoft.NET.Sdk.Web">
    <PropertyGroup>
        <TargetFramework>netstandard2.0</TargetFramework>
        <RunCommand>dotnet</RunCommand>
        <LangVersion>7.3</LangVersion>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="Microsoft.AspNetCore.Blazor.Build" Version="0.7.0" PrivateAssets="all" />
    </ItemGroup>
</Project>

These are the only dependencies needed. On build, the compatible assembly will be found in the /dist/_framework/_bin folder. It can then be loaded using the methods described in the question.

This works, but feels a bit hacky because the only reason we're turning the library into a web app is so that it can compile itself into a properly linked assembly.

2. Load the netstandard2.0 mono facade

Another solution is to unzip the Nuget Package from Microsoft.AspNetCore.Blazor.Build and grab the netstandard.dll. It's found in the tools\mono\bcl\Facades folder. Now, when doing the following in the main Blazor app:

var netstandard = await client.GetByteArrayAsync("http://localhost:62633/_framework/netstandard.dll");
var externallib = await client.GetByteArrayAsync("http://localhost:62633/_framework/MyCustomLib.dll");
AppDomain.CurrentDomain.Load(netstandard);
var assembly = AppDomain.CurrentDomain.Load(externallib);

then the unmodified netstandard 2.0 library MyCustomLib will be loaded without errors.

  • No need to change it to a web app
  • This works, but it feels even hackier than the first solution, unsure whether this will fail later along the way...

3. Use the Blazor Build tools

The Blazor Build tools, currently found here, they have a ResolveRuntimeDependenciesCommand command for the CLI which seems to do exactly what a blazor web app is doing when it spits output to /_framework/_bin. I'm still looking at how this could be used to convert a "non blazor-webapp" assembly into a mono-compatible one.

Feel free to comment or answer with additional information. I'm leaving this question open until a "cleaner" solution is found.

like image 40
Sigge Avatar answered Oct 13 '22 01:10

Sigge