asp.net-mvc - net - unity ioc c#
Un DbContext por solicitud en ASP.NET MVC(sin contenedor IOC) (6)
La pendiente resbaladiza aquí tiene un estado inconsistente. Si su aplicación va a tener múltiples usuarios y tienen el potencial de cambiar datos al mismo tiempo, entonces puede comenzar a tener problemas de integridad de datos si mantiene un contexto único.
Disculpe si ya se ha respondido, pero ¿cómo se garantiza un DbContext de Entity Framework por solicitud si no está utilizando un contenedor de IOC? (Las respuestas que he encontrado hasta ahora tratan con las soluciones de contenedores de IOC).
Parece que la mayoría de las soluciones entran en el diccionario HttpContext.Current.Items
, pero ¿cómo se garantiza la eliminación del DbContext cuando finaliza la solicitud? (¿O la eliminación no es absolutamente necesaria con un EF DbContext
?)
Editar
Actualmente estoy instanciando y eliminando mi DbContext en mis controladores, pero también tengo varias instancias separadas de mi DbContext en ActionFilters y mi MembershipProvider (y acabo de notar, también un par de validadores). Entonces, pensé que sería una buena idea centralizar la creación de instancias y el almacenamiento de mi DbContext para reducir la sobrecarga.
Pequeña adición para la respuesta de Chad Moran. Está inspirado en notas walther. Para evitar la inicialización del contexto para el contenido estático, debemos verificar el controlador de ruta actual (este ejemplo solo para MVC):
protected virtual void Application_BeginRequest()
{
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(this.Context));
if (routeData != null && routeData.RouteHandler is MvcRouteHandler)
{
HttpContext.Current.Items["_EntityContext"] = new EntityContext();
}
}
Sé que esta no es una pregunta reciente, pero publicaré mi respuesta de todos modos, porque creo que alguien puede encontrarla útil.
Como probablemente muchos otros, seguí los pasos mencionados en la respuesta aceptada. Sí, funciona. SIN EMBARGO , hay una trampa:
Los métodos BeginRequest () y EndRequest () se activan cada vez que se realiza una solicitud , pero no solo para las páginas aspx, ¡sino para TODO EL CONTENIDO ESTÁTICO! Dicho esto, si usa el código mencionado anteriormente y tiene en su página digamos 30 imágenes, ¡está volviendo a crear instancias de su contexto de db 30 veces!
La solución para esto es usar una clase de ajuste para recuperar el contexto, algo como esto:
internal static class ContextPerRequest
{
internal static DB1Entities Current
{
get
{
if (!HttpContext.Current.Items.Contains("myContext"))
{
HttpContext.Current.Items.Add("myContext", new DB1Entities());
}
return HttpContext.Current.Items["myContext"] as DB1Entities;
}
}
}
Y luego para deshacerse
protected void Application_EndRequest(object sender, EventArgs e)
{
var entityContext = HttpContext.Current.Items["myContext"] as DB1Entities;
if (entityContext != null)
entityContext.Dispose();
}
Esta modificación garantiza que instanciará y dispondrá de su contexto solo una vez por solicitud y solo cuando sea necesario. La respuesta seleccionada ejemplifica el contexto cada vez.
Nota: DB1Entities se deriva de DbContext (generado por VS). Probablemente quieras alterarlo con tu nombre de contexto;)
Nota 2: en este ejemplo, estoy trabajando con solo un dbcontext. Si necesita trabajar con múltiples, deberá modificar este código de acuerdo con sus necesidades. No tome esto como una solución definitiva a los problemas mundiales, porque ciertamente no es un producto final. Solo pretende dar una pista, cómo se puede lograr de una manera muy fácil.
Nota 3: el mismo enfoque también puede usarse en diferentes situaciones, por ejemplo, cuando desea compartir una instancia de SqlConnection o cualquier otra ... Esta solución no es exclusiva del objeto DbContext, ni del marco Entity.
Si implementa IDisposable en su controlador, y elimina el contexto en el método de eliminación, y crea una instancia de contexto nuevo en el constructor del controlador, debe estar seguro ya que el controlador se crea una instancia para cada solicitud. No veo, sin embargo, ¿por qué querrías hacer eso? ... Debe usar DI, o hacer una fábrica de contexto con una instancia estática de contexto. Si no usa una instancia (realiza una para cada solicitud), tendrá problemas en algún momento. El problema con el contexto no expuesto es que EF almacena en caché los datos en contexto, y si alguna otra instancia de contexto cambia algo en DB que ya está en caché en otro contexto, tiene un estado no consistente. Antes de que DI se hiciera tan popular, solía tener una instancia estática de contexto en alguna parte de la aplicación, y eso es mucho más rápido y seguro que tener cada solicitud en su propio contexto, pero es necesario implementar un código de comprobación de estado que asegure ese contexto la conexión a db está bien ... Hay muchas mejores soluciones para este problema, y lo mejor es usar algún marco DI. Recomendaría Ninject en combinación con MVCTurbine, es fácil de configurar y puedes agregarlo a través de NuGet.
Una forma sería suscribirse para el evento Application_BeginRequest
, inyectar el DbContext en el HttpContext actual y en la búsqueda de Application_EndRequest
de HttpContext y deshacerse de él. Cualquier cosa en el medio (que es prácticamente todo :-)) podría obtener el DbContext del HttpContext actual y usarlo. Y, sí, debes desecharlo. Y, por cierto, ¿hay alguna razón por la que no utilice un marco DI que ya lo haga por usted, entre otras cosas útiles?
Usaría el método BeginRequest / EndRequest, esto ayuda a garantizar que su contexto se elimine de forma adecuada cuando finalice la solicitud.
protected virtual void Application_BeginRequest()
{
HttpContext.Current.Items["_EntityContext"] = new EntityContext();
}
protected virtual void Application_EndRequest()
{
var entityContext = HttpContext.Current.Items["_EntityContext"] as EntityContext;
if (entityContext != null)
entityContext.Dispose();
}
Y en tu clase EntityContext ...
public class EntityContext
{
public static EntityContext Current
{
get { return HttpContext.Current.Items["_EntityContext"] as EntityContext; }
}
}