Ir al contenido principal

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.

Comentarios

Entradas más populares de este blog

Bloqueos

Una de las preguntas típicas de las juntas matutinas en los equipos de desarrollo de software es ¿Hay algún bloqueo? Si lo hay, se trata de ver qué es lo que está esperando esa persona y encontrar la forma de que se desbloquee; pero ¿Qué son los bloqueos? Los bloqueos son obstáculos que te impiden realizar o avanzar en tu trabajo. Evitan que puedas seguir progresando en el proyecto.

He notado que es común en las personas con menos experiencia decir que tienen un bloqueo cuando están batallando, debido a su poca experiencia, en la forma de resolver un problema. Han intentado varias formas y se empiezan a quedar sin ideas de como puede ser resuelto el problema o como pueden cumplir con el requerimiento especificado. Al quedarse sin opciones de qué intentar dicen que tienen un bloqueo con la tarea y que a menos que alguien les diga como resolverlo, no se puede avanzar en la tarea.

En personas con más experiencia, ese tipo de bloqueos no ocurren, una persona con experiencia ha visto pro…

Firebird 2.1 UPDATE OR INSERT

Another great feature that I like in Firebird 2.1 is the UPDATE OR INSERT statement. It's a really time saver and it makes the SQL cleaner.

For example suppose I have a products table like the one I use in my last post and an inventory table to store the product stock. Before Firebird 2.1 if I want to set the stock for a product I needed to check if a record for that product_id already exists; if the product_id already exists then I write an update. If not then I write an insert statement. So I ended up with something like this:


IF EXISTS(SELECT * FROM inventory WHERE product_id = :product_id ) THEN
UPDATE
inventory
SET
stock = :stock
WHERE
product_id = :product_id;
ELSE
INSERT INTO inventory
(product_id, stock)
VALUES
(:product_id, :stock);

In this example I only update one field but when I have to update a big table I ended up with a big chunk of code and thinking: "there should be another (better) way to do this".

Fortunately now with Firebird 2.1 there…

Database Mail en MS SQL Server 2005

Configuración de Database Mail en MS SQL Server 2005

Primero se debe de habilitar, ya que por omisión el componente esta deshabilitado, Utilizando el SSMS (SQL Server Management Studio)


Si no esta habilitado aparecerá un mensaje preguntado si lo habilita, después aparece esta ventana donde se pregunta al usuario que es lo desea hacer.


Seleccionamos la primera opción para crear un perfil.


Configuramos el perfil y le agregamos por lo menos una cuenta.


Seleccionamos el perfil como public y default.


Para mandar correo se utiliza el procedimiento msdb.sp_send_dbmail por lo tanto el usuario que intente mandar correo debe de tener permiso para la base de datos msdb.

Referencias:
http://www.sqlservercentral.com/columnists/cBunch/introtodatabasemailinsql2005.asp