Lazy load assemblies in a Blazor WebAssembly application

 
 
  • Gérald Barré

When a Blazor WebAssembly application starts, it downloads all DLLs upfront. This means that even a DLL used only on a single page is loaded at startup. To improve startup performance, you can defer loading some assemblies until they are actually needed.

#Lazy loading assemblies

First, you need to edit the project file (csproj) and add the list of lazy-loaded assemblies using <BlazorWebAssemblyLazyLoad>:

csproj (MSBuild project file)
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
    <RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
    <UseBlazorWebAssembly>true</UseBlazorWebAssembly>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.0-preview.8.20414.8" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.0-preview.8.20414.8" PrivateAssets="all" />
    <PackageReference Include="YamlDotNet" Version="8.1.2" />
  </ItemGroup>

  <ItemGroup>
    <BlazorWebAssemblyLazyLoad Include="YamlDotNet" />
  </ItemGroup>

</Project>

This will change the way the blazor.boot.json file is generated. You should see a new section named lazyAssembly:

Now if you navigate to a page that needs the DLL, you'll get an error because the DLL is not loaded:

blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Could not load file or assembly 'YamlDotNet, Version=8.0.0.0, Culture=neutral, PublicKeyToken=ec19458f3c15af5e' or one of its dependencies.
System.IO.FileNotFoundException: Could not load file or assembly 'YamlDotNet, Version=8.0.0.0, Culture=neutral, PublicKeyToken=ec19458f3c15af5e' or one of its dependencies.
File name: 'YamlDotNet, Version=8.0.0.0, Culture=neutral, PublicKeyToken=ec19458f3c15af5e'
   at OnlineTools.Pages.YamlToJson.BuildRenderTree(RenderTreeBuilder __builder) in C:\Users\meziantou\source\repos\meziantou\online-tools\OnlineTools\Pages\YamlToJson.razor:line 0
   at Microsoft.AspNetCore.Components.ComponentBase.<.ctor>b__6_0(RenderTreeBuilder builder)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()

To fix this error, load the DLL before navigation occurs. Use the OnNavigateAsync router event to intercept navigation and load the required assembly on demand. Edit the App.razor component as follows:

Razor
@inject Microsoft.AspNetCore.Components.WebAssembly.Services.LazyAssemblyLoader AssemblyLoader

<Router AppAssembly="@typeof(Program).Assembly"
        OnNavigateAsync="OnNavigateAsync"
        AdditionalAssemblies="lazyLoadedAssemblies">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

@code{
    private List<System.Reflection.Assembly> lazyLoadedAssemblies = new List<System.Reflection.Assembly>();

    private async Task OnNavigateAsync(NavigationContext context)
    {
        if (context.Path == "yaml-to-json") // Url of the page that needs the lazy loaded assembly
        {
            var assemblies = await AssemblyLoader.LoadAssembliesAsync(new[] { "YamlDotNet.dll" });
            lazyLoadedAssemblies.AddRange(assemblies);
        }
    }
}
  • AssemblyLoader.LoadAssembliesAsync will fetch the assemblies requested via a network call and load them into the runtime.
  • AdditionalAssemblies is optional. It configures the router to search components that can match URIs in the lazy loaded assemblies. If the lazy-loaded DLLs don't contain any component, you can remove it.

#Showing loading indicator when loading DLLs

Loading lazy assemblies can take a moment. During this time, you can display a loading indicator to let the user know something is happening.

Razor
<Router AppAssembly="@typeof(Program).Assembly" OnNavigateAsync="OnNavigateAsync" AdditionalAssemblies="lazyLoadedAssemblies">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
    <Navigating>
        <p>Loading the page...</p>
    </Navigating>
</Router>

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?