49 votes

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

Je m'excuse si cette question a déjà été traitée, mais comment garantir un DbContext d'Entity Framework par requête si vous n'utilisez pas de conteneur IOC ? (Les réponses que j'ai trouvées jusqu'à présent traitent des solutions de conteneurs IOC).

Il semble que la plupart des solutions s'accrochent au HttpContext.Current.Items mais comment garantir l'élimination du DbContext lorsque la requête est terminée ? (Ou bien l'élimination n'est-elle pas absolument nécessaire avec un EF DbContext ?)

Modifier

Je suis actuellement en train d'instancier et de disposer mon DbContext dans mes contrôleurs, mais j'ai aussi plusieurs instanciations séparées de mon DbContext dans les 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 l'overhead.

73voto

walther Points 7554

Je sais que cette question n'est pas récente, mais je vais quand même poster ma réponse, car je pense que quelqu'un pourra la trouver utile.

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

Méthodes BeginRequest() et EndRequest() se déclenchent à chaque fois qu'une demande est faite mais pas seulement pour les pages aspx, mais pour TOUT LE CONTENU STATIQUE ! 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 pour cela est d'utiliser une classe de recouvrement 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 ensuite 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 permet de s'assurer que vous n'instanciez et ne disposiez de votre contexte qu'une seule fois par demande et uniquement lorsque cela est nécessaire. La réponse sélectionnée instancie le contexte à chaque fois.

Note : 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 du monde, car ce n'est certainement pas un produit final. Il s'agit simplement de donner un aperçu de la manière dont on peut y parvenir très facilement.

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

60voto

Chad Moran Points 8560

J'utiliserais la méthode BeginRequest/EndRequest, qui permet de s'assurer que le contexte est éliminé correctement lorsque la requête 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; }
    }
}

10voto

Darin Dimitrov Points 528142

Un moyen serait de s'abonner à la Application_BeginRequest injecter le DbContext dans le HttpContext actuel et dans l'événement Application_EndRequest récupère dans le HttpContext et dispose. Tout ce qui se trouve entre les deux (c'est-à-dire à peu près tout :-)) peut récupérer le DbContext à partir du HttpContext actuel et l'utiliser. Et, oui, vous devez l'éliminer. Et d'ailleurs, y a-t-il une raison pour laquelle vous n'utilisez pas un framework DI qui fait déjà cela pour vous parmi d'autres choses utiles ?

7voto

DrunkCoder Points 2307

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 devrions vérifier le gestionnaire de route actuel (cet exemple ne concerne que 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();
  }
}

1voto

Goran Obradovic Points 4374

Si vous implémentez IDisposable dans votre contrôleur, et disposez du contexte dans la méthode de disposition, et instanciez le nouveau contexte dans le constructeur du contrôleur, vous devriez être en sécurité puisque le contrôleur est instancié pour chaque requête. Je ne vois pas, cependant, pourquoi vous voudriez faire cela ? ... Vous devriez utiliser DI, ou créer une usine de contexte avec une instance statique de contexte. Si vous n'utilisez pas une seule instance (vous en créez une pour chaque requête), vous allez avoir 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 du contexte modifie quelque chose dans la base de données qui est déjà en cache dans un autre contexte - vous avez un état non cohérent. Avant que DI ne devienne si populaire, j'avais l'habitude d'avoir une instance statique de contexte quelque part dans l'application, et c'est beaucoup plus rapide et plus sûr que d'avoir chaque requête qui crée son propre contexte, mais vous devez implémenter un code de vérification d'état qui s'assure que la connexion du contexte à la base de données est correcte... Il y a beaucoup de meilleures solutions à ce problème, et la meilleure est d'utiliser un framework DI. Je recommande Ninject en combinaison avec MVCTurbine, il est facile à mettre en place et vous pouvez l'ajouter via NuGet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X