jueves, mayo 20, 2010

Singleton por cada HTTP Request usando Unity IoC

Hay ocasiones en las que necesitamos tener un objeto compartido por varios objetos en nuestra aplicacion web, similar al patrón singleton, pero solo para esa petición HTTP. Es decir sin compartirlo en toda la aplicación.

Hace tiempo escribí sobre como compartir un LinqToSql DataContext por cada request en una aplicación web. Ahora utilizando Unity podemos lograr ese comportamiento y dejar que el contenedor de dependencias se encargue de darnos la instancia adecuada.

Para manejar el tiempo de vida de las instancias unity utiliza lo que llama LifetimeManager, entonces lo primero que debemos hacer es crear nuestro LifetimeManager, en este caso lo llamaré HttpRequestLifetimeManager y heredamos de la clase abstracta Microsoft.Practices.Unity.LifetimeManager y debemos de definir 3 metodos GetValue, RemoveValue y SetValue.

public class HttpRequestLifetimeManager: LifetimeManager
{
object key;

public HttpRequestLifetimeManager(): this(null)
{
}

public HttpRequestLifetimeManager(object key)
{
this.key = key;
}

public override object GetValue()
{
if (key == null) return null;
return HttpContext.Current.Items[key];
}

public override void RemoveValue()
{

}

public override void SetValue(object newValue)
{
if (key == null)
key = newValue.GetType();

HttpContext.Current.Items[key] = newValue;
}
}


Lo que hace el HttpRequestLifetimeManager es guardar la instancia de la clase en el Items dictionary con el key que se le especifique en el constructor, si no se ha especificado un key, entonces utilizará el tipo del objeto para identificarlo en el diccionario.


Una vez teniendo nuestro LifetimeManager solo es cuestión de especificarle a unitity, al momento de registrar nuestro tipo, que utilice nuestro HttpRequestLifetimeManager para manejar el tiempo de vida de nuestra instancia.


Por ejemplo supongamos que tenemos declarados algunas clases (posiblemente repositorios) que recibe el constructor un DataContext algo como esto:


public class UserRepository : IUserRepository       
{
readonly MyDataContext db;

public UserRepository(MyDataContext db)
{
this.db = db;
}
...

Si queremos que todos nuestros repositorios compartan el mismo MyDataContext por cada request, entonces configuramos nuestro contenedor (generalmente en el Global.asx.cs) de la siguiente manera:


var container = new UnityContainer();                             

container.RegisterType<IAccountRepository, AccountRepository>();
container.RegisterType<IUserRepository, UserRepository>();

container.RegisterType<MyDataContext>(
new HttpRequestLifetimeManager(), new InjectionConstructor());

Ahí podemos ver como al registrar MyDataContext paso una instancia de la clase HttpRequestLifetimeManager para usar la misma instancia durante todo el request y además le paso un instancia de InjectionConstructor para indicarle que utilice el constructor sin argumentos de nuestra clase MyDataContext.


Una vez hecho esto nuestro contenedor se encargará de entregarnos repositorios que comparten el mismo datacontext por cada request.

No hay comentarios.:

Publicar un comentario