[forCode]

DI con Autofac: II – Ciclos de vida

Después de ver un primer vistazo rápido en el post anterior, en este veremos un caso práctico y los tiempos de vida que podemos asignarles a nuestros objetos cuando los resolvemos.

DI con Autofac: II – Ciclos de vida

Después de ver un primer vistazo rápido en el post anterior, en este veremos un caso práctico y los tiempos de vida que podemos asignarles a nuestros objetos cuando los resolvemos.

Ejemplo rápido

En el cliente donde estoy, hubo una determinada situación donde el implementar Autofac nos salvaría. Aquí tenemos una aplicación MVC y siempre se tienen que filtrar los datos por determinado Id (TenantId). El usuario hace login, la aplicación se trae el TenantId al cual pertenece y se guarda en la cookie la identificación del usuario junto con el TenantId. Este es el concepto de multi-tenant(más o menos) que habréis visto en otras ocasiones, y si no, aquí tenéis una explicación.

Siempre que el model herede de determinada interfaz tiene que filtrar los datos cuando hace la petición a BDD, pero cuando los guarda, tiene que asegurarse de que se guarda con ese id.

Cuando lo registramos en el IoC (Inversion Of Control), le ponemos que sea perRequest para que cuando se haga una petición a un controller, se le inyecte un IUsuario con el TenantId y en la función XXXX se asigna a IUsuario los valores de la cookie. En los repositorios inyectamos el IUsuario (más bien en un base repository) y así tenemos los datos para poder filtrar el contenido.

En el repositorio como ejemplo hacemos una función de Add genérica tal que:

public T Add(T item, ...)
{
    if (item is BaseTenant)
    {
        (item as BaseTenant).TenantId = this.TenantId;
    }

    return this.GetSet().Add(item);
}

No es exactamente lo que tenemos en nuestra aplicación, pero es parecido.

Ciclos de vida

Con ciclos de vida me refiero a cuánto tiempo puedo acceder a la misma instancia. En Web es muy claro, cuando haces una petición al backend, un ciclo de vida es desde que haces la petición hasta que recibes la respuesta (aunque sea el http Ok).

Para gestionar un ciclo de vida con DI (Dependency Injection) tenemos varios tipos a nuestra disposición:

Intance per Dependency

var builder = new ContainerBuilder();
builder.Register<Tenant>().As<ITenant>().InstancePerDependency();

Aquí estamos indicando que la interfaz ITenant se resuelve con el objeto Tenant y que el ciclo de vida es una instancia por dependencia. Esto significa que en cada constructor y cada vez que a mano pidamos que nos resuelva ITenant, nos dará una nueva instancia del objeto, de tal manera que nunca tendremos datos modificados por otros.

Single Instance

Este, es más conocido como Singleton, en resumen es que la primera vez que resolvamos ITenant lo instanciará y después siempre nos devolverá la misma instancia. En un post de Alejandro de forCode explica más al detalle el patrón Singleton.

Este es un ejemplo de cómo se registraría:

var builder = new ContainerBuilder();
builder.Register<Tenant>().As<ITenant>().SingleInstance();

Y el ejemplo comentado sería este:

using(var scope1 = container.BeginLifetimeScope())
{
  for(var i = 0; i < 100; i++)
  {
    // Siempre que intentamos obtener una instancia de Worker
    // en este scope obtenemos la misma instancia
    var w1 = scope1.Resolve<Worker>();
  }
}

using(var scope2 = container.BeginLifetimeScope())
{
  for(var i = 0; i < 100; i++)
  {

    // Siempre que intentamos obtener una instancia de Worker
    // en este scope obtenemos la misma instancia, pero esta
    // instancia es diferente que la del anterior scope
    var w2 = scope2.Resolve<Worker>();
  }
}

Instance Per Matching Lifetime Scope

Es exactamente lo mismo que Instance Per Lifetime Scope, pero con un detalle que nos puede ir bastante bien. Registramos el tenant tal que:

var builder = new ContainerBuilder();
builder.Register<Tenant>().As<ITenant>().InstancePerMatchingLifetimeScope("MiScope");

Cuando usamos el using (por ejemplo) podemos darle un nombre:

using(var scope1 = container.BeginLifetimeScope("MiScope"))

Si tenemos 1 o varios Scope con el nombre "MiScope", la instancia de Tenant será la misma en estos.

Instance Per Request

Este lo vemos muy claro en un entorno web. En MVC o WebAPI, el ciclo de vida de la request es desde el momento en que el backend recibe la petición al servidor, hasta que manda una respuesta (aunque será un http Ok). Básicamente, el Instance Per Request es este, desde que entra la petición hasta que sale la respuesta.

Para registrarlo sería:

var builder = new ContainerBuilder();
builder.Register<Tenant>().As<ITenant>().InstancePerRequest();

Trabajando con Threads

Este es para que lo tengáis en cuenta. El ciclo de vida de un Thread, como os imaginaréis, es desde que inicias el thread hasta que se hace un dispose.

Dentro de este thread, si resolvemos una instancia de ITenant, el ciclo de vida será dentro de ese Thread y nada más. Por supuesto tenemos que tener en cuenta cuando hacemos el destroy de este Thread para que no nos perjudique.

Y hasta aquí todo sobre Ciclos de Vida con Autofac.

¡Saludos!


Dependency Injection con Autofac

Compartir en: