web-dev-qa-db-fra.com

Un DbContext par requête dans ASP.NET MVC (sans IOC)

Toutes mes excuses si cela a déjà été répondu, mais comment garantir un Entity Framework DbContext par requête si vous n'utilisez pas un conteneur IOC? (Les réponses que j'ai rencontrées jusqu'à présent concernent = IOC solutions de conteneurs.)

Il semble que la plupart des solutions se connectent au HttpContext.Current.Items dictionnaire, mais comment garantir l'élimination du DbContext une fois la demande terminée? (Ou l'élimination n'est-elle pas absolument nécessaire avec un EF DbContext?)

Modifier

J'instancie et jette actuellement mon DbContext dans mes contrôleurs, mais j'ai également plusieurs instanciations distinctes de mon DbContext dans ActionFilters et mon MembershipProvider (et je viens de remarquer, également quelques validateurs). J'ai donc pensé que ce serait une bonne idée de centraliser l'instanciation et le stockage de mon DbContext pour réduire les frais généraux.

49
devuxer

J'utiliserais la méthode BeginRequest/EndRequest, cela permet de garantir que votre contexte est correctement supprimé lorsque la demande est terminée.

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();
}

Et dans votre classe EntityContext ...

public class EntityContext
{
    public static EntityContext Current
    {
        get { return HttpContext.Current.Items["_EntityContext"] as EntityContext; }
    }
}
60
Chad Moran

Je sais que ce n'est pas une question récente, mais je publierai quand même ma réponse, car je pense que quelqu'un peut la trouver utile.

Comme probablement beaucoup d'autres, j'ai suivi les étapes mentionnées dans la réponse acceptée. Ouais, ça marche. CEPENDANT , il y a un hic:

Méthodes BeginRequest () et EndRequest () se déclenche chaque fois qu'une demande est faite, mais pas seulement pour les pages aspx, mais pour TOUS LES CONTENUS STATIQUES! Cela dit, si vous utilisez le code mentionné ci-dessus et que vous avez sur votre page disons 30 images, vous réinstanciez votre dbcontext 30 fois!

La solution consiste à utiliser une classe d'habillage pour récupérer le contexte, quelque chose comme ceci:

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;
          }
      }
 }

Et puis pour disposer

protected void Application_EndRequest(object sender, EventArgs e)
{
   var entityContext = HttpContext.Current.Items["myContext"] as DB1Entities;
   if (entityContext != null) 
      entityContext.Dispose();
}

Cette modification garantit que vous instanciez et supprimez votre contexte une seule fois par demande et uniquement en cas de besoin. La réponse sélectionnée instancie le contexte à chaque fois.

Remarque: DB1Entities est dérivé de DbContext (généré par VS). Vous voudrez probablement le modifier avec votre nom de contexte;)

Note 2: dans cet exemple, je travaille avec un seul dbcontext. Si vous devez travailler avec plusieurs, vous devrez modifier ce code en fonction de vos besoins. Ne prenez pas cela comme une solution ultime aux problèmes mondiaux, car ce n'est certainement pas un produit final. Il est juste destiné à donner un indice, comment il peut être réalisé de manière très simple.

Note 3: La même approche peut également être utilisée dans différentes situations, par exemple lorsque vous souhaitez partager une instance de SqlConnection ou toute autre ... Cette solution n'est pas exclusive à l'objet DbContext, ni au cadre Entity.

73
walther

Une façon serait de souscrire au Application_BeginRequest, injectez le DbContext dans le HttpContext actuel et dans le Application_EndRequest récupérer à partir du HttpContext et éliminer. Tout ce qui se trouve entre les deux (ce qui est à peu près tout :-)) pourrait récupérer le DbContext du HttpContext actuel et l'utiliser. Et, oui, vous devez le jeter. Et au fait, y a-t-il une raison pour laquelle vous n'utilisez pas un framework DI qui le fait déjà pour vous, entre autres choses utiles?

10
Darin Dimitrov

Petit ajout pour la réponse de Chad Moran. Il est inspiré des notes de walther. Pour éviter l'initialisation du contexte pour le contenu statique, nous devons vérifier le gestionnaire d'itinéraire actuel (cet exemple uniquement pour 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();
  }
}
7
DrunkCoder

Si vous implémentez IDisposable dans votre contrôleur, et supprimez le contexte dans la méthode de suppression, et instanciez le nouveau contexte dans le constructeur du contrôleur, vous devez être en sécurité car le contrôleur est instancié pour chaque demande. Je ne vois pas, cependant, pourquoi voudriez-vous faire cela? ... Vous devez utiliser DI ou créer une fabrique de contexte avec une instance statique de contexte. Si vous n'utilisez pas une seule instance (vous en créez une pour chaque demande), vous rencontrez des problèmes à un moment donné. Le problème avec le contexte non disposé est que EF met en cache les données dans le contexte, et si une autre instance de contexte change quelque chose dans la base de données qui est déjà mis en cache dans un autre contexte - vous avez un état incohérent. Avant que DI ne devienne si populaire, j'avais une instance statique de contexte quelque part dans l'application, ce qui est beaucoup plus rapide et plus sûr que de demander à chaque demande de créer son propre contexte, mais vous devez implémenter un code de vérification d'état qui garantit que ce contexte la connexion à db est ok ... Il existe de bien meilleures solutions à ce problème, et le mieux est d'utiliser un cadre DI. Je recommanderais Ninject en combinaison avec MVCTurbine, il est facile à configurer et vous pouvez l'ajouter via NuGet.

1
Goran Obradovic

La pente glissante ici a un état incohérent. Si votre application compte plusieurs utilisateurs et qu'ils peuvent modifier les données simultanément, vous risquez de rencontrer des problèmes d'intégrité des données si vous conservez un seul contexte.

0
mymex1