es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Llame al método asíncrono desde un método no asíncrono en Blazor WebAssembly.

Estoy tratando de desarrollar una aplicación extensible con Blazor WASM, que descarga complementos desde el servidor y los almacena en la memoria del navegador. Tanto las API de descarga como las de caché son asíncronas.

El siguiente AssemblyLoadContext es responsable de cargar los ensamblados del complemento. Dado que los complementos pueden contener múltiples archivos de ensamblado, y como el proyecto debería admitir la carga diferida de ensamblados referenciados, estoy manejando el evento Resolving de la siguiente manera:

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();            // <- ¡El problema!
                    assembly = task.Result; // <- ¡El problema!
                }
            }

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

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

El problema es que no puedo llamar a la API asíncrona desde este método, ya que lanza una excepción que indica “No se puede esperar en monitores en esta ejecución.”, y no he podido averiguar cómo debería llamarlos de forma síncrona.

Gracias por cualquier ayuda.

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Como sugiere el mensaje de error, llamar a la API asíncrona en un método sincrónico causa una espera bloqueante y no está permitido en tiempo de ejecución. Una solución posible es convertir el método ResolveAssembly en un método asíncrono y usar las palabras clave async/await para manejar las operaciones asíncronas. Aquí tienes un ejemplo actualizado:

    private async Task<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)
                {
                    assembly = await task; // esperar la tarea en lugar de llamar a Wait() y acceder a la propiedad Result
                }
            }
    
            if (assembly == null)
            {
                // TODO: Registrar...
            }
    
            return assembly;
        }
        catch (Exception ex)
        {
            // TODO: Registrar...
            throw;
        }
    }
    

    Al convertir ResolveAssembly en un método asíncrono, se pueden utilizar las palabras clave async/await para esperar el resultado del método ResolveAsync en lugar de llamar a Wait() y acceder a la propiedad Result. Esto permite manejar correctamente las operaciones asíncronas y evita el problema de sincronización.</assembly?>

Comments are closed.