Vahid Rassouli
Vahid Rassouli

Reputation: 296

Call async method from non-async method in blazor web assembly

I'm trying to develop an extensible application with Blazor WASM, which downloads plugins from server, and caches them at browser storage. Both downloading and caching APIs are async.

The following AssemblyLoadContext, is responsible to load plugin assemblies. As plugins may contain multiple assembly files, and as the project should support lazy loading of referenced assemblies, I'm handling the Resolving event as follow:

public class PluginAssemblyLoadContext : AssemblyLoadContext
{
    private readonly IPluginResolver? _pluginResolver;

    public PluginAssemblyLoadContext(string name) : this(name, null)
    {

    }

    public PluginAssemblyLoadContext(string name, IPluginResolver? pluginResolver) : base(name, isCollectible: true)
    {
        _pluginResolver = pluginResolver;

        if (pluginResolver != null)
        {
            Resolving += ResolveAssembly;
        }
    }

    protected override Assembly? Load(AssemblyName name)
    {
        return null;
    }

    private Assembly? ResolveAssembly(AssemblyLoadContext context, AssemblyName assemblyName)
    {
        try
        {
            var assembly = AssemblyLoadContext.Default.Assemblies.FirstOrDefault(x => x.GetName().Name == assemblyName.Name);

            if (assembly == null)
            {
                var task = _pluginResolver?.ResolveAsync(context, assemblyName);
                if (task != null)
                {
                    task.Wait();            // <- The problem!
                    assembly = task.Result; // <- The problem!
                }
            }

            if (assembly == null)
            {
                // TODO: Log...
            }

            return assembly;
        }
        catch (Exception ex)
        {
                // TODO: Log...
            throw;
        }
    }
}

The problem is that I can't call the async API from this method as it throws an exception telling "Cannot wait on monitors on this runtime." and I couldn't figure out how should I call them synchronously.

Thanks for any help

Upvotes: 1

Views: 1560

Answers (3)

Pieterjan
Pieterjan

Reputation: 3521

I'm afraid that the AssemblyLoadContext doesn't really leave you with any other option. In ASP.NET Core for example, when you want to write a middleware, you do this:

app.Use((context, next) =>
{
    return next();
});

In this case the following overload is called:

Overload with a synchronous callback function

Now if you need to do an async call, you can do so:

app.Use(async (context, next) =>
{
    await next();
});

But this is only possible because ASP.NET Core provides you with an overload for Use (where the callback parameter is usually of type Func<Task>):

Overload with an asynchronous callback function

So the bottom line is: AFAIK it's not possible, as long as AssemblyLoadContext doesn't provide events with Task<Assembly> return type.

Your best bet is probably to create an issue on Github.

Upvotes: 0

Christian Reizner
Christian Reizner

Reputation: 56

Is it possible to rewrite this into async-await? That's to only async way, Blazor Webassambly can handle at the moment. Or a more dirty way: Call a js function, that calls back

[JSInvokable] public async Task<Assembly?> GetTaskResult(Task task)
    => await task;

Upvotes: 0

GoodboY
GoodboY

Reputation: 309

It seems like you are trying to block the main execution thread by doing.Wait() call. This is bad because the main thread may be responsible for handling UI updates. If it is blocked - the UI of the application will be frozen while your task is in progress. I believe this is the main reason you are getting this error.

The solution I may propose - do not wait till the task is finished. Instead, add a callback to it via .ContinueWith(). In that callback, you may handle the result of the execution and finish your business logic (in your case - perform logging).

Upvotes: 1

Related Questions