Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 1 de diciembre de 2020
Blazor

Una pequeña pero interesante novedad introducida en Blazor 5, es la posibilidad de escribir nuestras propias factorías de componentes, o, como les llaman en el framework, Component Activators.

El Component Activator es un objeto singleton que implementa la interfaz IComponentActivator, cuyo único requisito es el cumplimiento del siguiente contrato:

public interface IComponentActivator
{
    IComponent CreateInstance(Type componentType);
}

Como se puede intuir, Blazor invocará al activator registrado en el sistema justo en el momento en que va a instanciar un componente: se le suministra el tipo como parámetro, y retornará ya un objeto IComponent, la interfaz más básica que cumplen todos los componentes Blazor.

Por defecto, se utiliza como activador la clase interna DefaultComponentActivator:

internal class DefaultComponentActivator : IComponentActivator
{
    public static IComponentActivator Instance { get; } = new DefaultComponentActivator();

    public IComponent CreateInstance(Type componentType)
    {
        var instance = Activator.CreateInstance(componentType);
        if (!(instance is IComponent component))
        {
            throw new ArgumentException($"The type {componentType.FullName}" 
              + $"does not implement {nameof(IComponent)}.", nameof(componentType));
        }

        return component;
    }
}

Obviamente, si quisiéramos intervenir en el proceso de construcción de componentes, sólo tendremos que implementar nuestra clase personalizada y registrarla como singleton en el inyector de dependencias, asociada a la interfaz IComponentActivator.

Para crear nuestra propia implementación, fijaos que la clase DefaultComponentActivator es interna y no podemos heredar de ella, pero tiene un código tan simple que podemos usarlo para crear nuestras propias versiones.

Veamos un par de ejemplos.

Ejemplo 1: Loggeador de creación de componentes

La siguiente clase, registrada de la forma services.AddSingleton<IComponentActivator, ComponentActivatorLogger>() realiza la misma tarea que la implementación por defecto, pero deja rastro en el canal de depuración de las instancias que va creando, permitiéndo contarlas:

public class ComponentActivatorLogger : IComponentActivator
{
    public int Count { get; set; }
    public IComponent CreateInstance(Type componentType)
    {
        var instance = Activator.CreateInstance(componentType);
        if (!(instance is IComponent component))
        {
            throw new ArgumentException($"The type {componentType.FullName} "
                  + $"does not implement {nameof(IComponent)}.", nameof(componentType));
        }
        Count++;
        Debug.WriteLine($"Created component #{Count}: {component}");
        return component;
    }
}

Por ejemplo, ese sería el resultado mostrado en la ventana de depuración tras acceder a la página principal:

Created component #1: BlazorApp11.App
Created component #2: Microsoft.AspNetCore.Components.Routing.Router
Created component #3: Microsoft.AspNetCore.Components.RouteView
Created component #4: Microsoft.AspNetCore.Components.LayoutView
Created component #5: BlazorApp11.Shared.MainLayout
Created component #6: BlazorApp11.Shared.NavMenu
Created component #7: BlazorApp11.Pages.Index
Created component #8: Microsoft.AspNetCore.Components.Routing.NavLink
Created component #9: Microsoft.AspNetCore.Components.Routing.NavLink
Created component #10: Microsoft.AspNetCore.Components.Routing.NavLink
Created component #11: BlazorApp11.Shared.SurveyPrompt

Ojo: si estamos usando prerenderización en servidor en Blazor Server, veremos que los componentes se crean dos veces.

Ejemplo 2: Construir un componente u otro en función de algún criterio

En este caso, vamos a hacer algo más bestia. Cuando se vaya a instanciar un componente de tipo MyComponent, se decidirá aleatoriamente si se retorna en su lugar una instancia de AnotherComponent.

public class WeirdComponentActivator : IComponentActivator
{
    private Random rnd = new Random();
    public IComponent CreateInstance(Type componentType)
    {
        if (componentType.IsAssignableFrom(typeof(MyComponent)))
        {
            if (rnd.Next(0, 10) % 2 == 0)
            {
                componentType = typeof(AnotherComponent);
            }
        }
        var instance = Activator.CreateInstance(componentType);
        if (!(instance is IComponent component))
        {
            throw new ArgumentException($"The type {componentType.FullName} "
                  + $"does not implement {nameof(IComponent)}.", nameof(componentType));
} return component; } }

¿Y es útil esto? Pues dudo que sea algo que vayáis a utilizar en el día a día, pero siempre está bien conocer los mecanismos que hay bajo el capó, por si en alguna ocasión fuera necesario tirar de ellos.

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!