asp.net - type - Singleton Per Call Context(solicitud web) en Unity
unity container example (3)
Gracias por su contribución,
Pero la implementación de la pregunta propuesta tiene dos inconvenientes, uno de los cuales es un error grave, como ya dijo Steven Robbins en su respuesta y Micah Zoltu en un comentario .
- Asp.net no garantiza que el contexto de la llamada se mantenga para una sola solicitud. Bajo carga, puede cambiar a otra, causando interrupciones en la implementación propuesta.
- No maneja la liberación de dependencias al final de la solicitud.
Actualmente, el paquete Unity.Mvc Nuget suministra un PerRequestLifetimeManager
para hacer el trabajo. No se olvide de registrar su UnityPerRequestHttpModule
asociado en el código de arranque, de lo contrario tampoco se manejará la liberación de dependencias.
Utilizando bootstrapping
DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
o en web.config system.webServer/modules
<add name="UnityPerRequestHttpModule" type="Microsoft.Practices.Unity.Mvc.UnityPerRequestHttpModule, Microsoft.Practices.Unity.Mvc" preCondition="managedHandler" />
Parece que su implementación actual también es adecuada para formularios web. Y ni siquiera depende de MVC. Desafortunadamente, su montaje lo hace, causa de algunas otras clases que contiene.
Tenga cuidado, en caso de que use algún módulo http personalizado que utilice sus dependencias resueltas, es posible que ya estén disponibles en el módulo EndRequest
. Depende del orden de ejecución del módulo.
Hace unos días tuve este problema con los subprocesos de ASP.Net. Quería tener un objeto singleton por solicitud web. Realmente necesito esto para mi unidad de trabajo. Quería crear una instancia de una unidad de trabajo por solicitud web para que el mapa de identidad sea válido en toda la solicitud. De esta manera, podría usar un IoC para inyectar mi propia IUnitOfWork a mis clases de repositorio de forma transparente, y podría usar la misma instancia para consultar y luego actualizar mis entidades.
Ya que estoy usando Unity, usé por error PerThreadLifeTimeManager. Pronto me di cuenta de que el modelo de subprocesamiento de ASP.Net no es compatible con lo que quiero lograr. Básicamente, utiliza un theadpool y recicla subprocesos, y eso significa que obtengo un UnitOfWork por subproceso. Sin embargo, lo que quería era una unidad de trabajo por solicitud web.
Un poco de google me dio este gran post . Eso era exactamente lo que quería; excepto por la parte de la unidad que era bastante fácil de lograr.
Esta es mi implementación para PerCallContextLifeTimeManager para unity:
public class PerCallContextLifeTimeManager : LifetimeManager
{
private const string Key = "SingletonPerCallContext";
public override object GetValue()
{
return CallContext.GetData(Key);
}
public override void SetValue(object newValue)
{
CallContext.SetData(Key, newValue);
}
public override void RemoveValue()
{
}
}
Y, por supuesto, lo uso para registrar mi unidad de trabajo con un código similar a este:
unityContainer
.RegisterType<IUnitOfWork, MyDataContext>(
new PerCallContextLifeTimeManager(),
new InjectionConstructor());
Espero que le ahorre un poco de tiempo a alguien.
Si bien está bien llamar a este PerCallContextLifeTimeManager, estoy bastante seguro de que no es "seguro" que se considere un LifeTimeManager por solicitud de ASP.Net.
Si ASP.Net realiza su intercambio de hilos, lo único que se transfiere al nuevo hilo a través de CallContext es el actual HttpContext: todo lo que almacene en CallContext desaparecerá. Esto significa que, bajo una carga pesada, el código anterior podría tener resultados inesperados, ¡y me imagino que sería un verdadero dolor rastrear por qué!
La única forma "segura" de hacerlo es con HttpContext.Current.Items, o haciendo algo como:
public class PerCallContextOrRequestLifeTimeManager : LifetimeManager
{
private string _key = string.Format("PerCallContextOrRequestLifeTimeManager_{0}", Guid.NewGuid());
public override object GetValue()
{
if(HttpContext.Current != null)
return GetFromHttpContext();
else
return GetFromCallContext();
}
public override void SetValue(object newValue)
{
if(HttpContext.Current != null)
return SetInHttpContext();
else
return SetInCallContext();
}
public override void RemoveValue()
{
}
}
Esto obviamente significa tomar dependencias en System.Web :-(
Mucha más información sobre esto disponible en:
http://piers7.blogspot.com/2005/11/threadstatic-callcontext-and_02.html
Solución ordenada, pero cada instancia de LifetimeManager debe usar una clave única en lugar de una constante:
private string _key = string.Format("PerCallContextLifeTimeManager_{0}", Guid.NewGuid());
De lo contrario, si tiene más de un objeto registrado con PerCallContextLifeTimeManager, están compartiendo la misma clave para acceder a CallContext, y no recuperará el objeto esperado.
También vale la pena implementar RemoveValue para asegurar que se limpien los objetos:
public override void RemoveValue()
{
CallContext.FreeNamedDataSlot(_key);
}