Entity Framework et référence au dataContext pour une application web

Nov 7, 2013

Lorsque l’on utilise l’entity framework et un datacontext on se pose souvent la question suivante :

Faut-il favoriser la performance et la simplicité d’utilisation ou bien la flexibilité (scalability) ?

(non ? vous ne vous posez pas ce genre de question existentielle ?)

Je détaille ma question :

Référence statique

Dans un premier cas vous faites une référence static au contexte, avec certaines fois du lazy loading et/ou une gestion du dispose dans le global.asax (évènement application end).

C’est performant (dans le sens c’est rapide) car selon comment c’est codé le contexte est instancié en même temps que le domaine applicatif (quand le pool démarre) ou bien au premier appel.

L’instance est ensuite détruite quand l’application s’arrête (reset iis, du pool, etc).

L’avantage c’est que pour chacun des appels de page, le model est déjà en mémoire, Entity Framework n’a donc pas à re-schématiser le modèle en mémoire et à vérifier la cohérence du modèle avec la base de donnée.

L’autre avantage c’est que vous travaillez avec une seule instance de contexte tout au long de la génération de la page, et que donc vous pouvez utiliser des entités liées (donc propriétés de navigation et tout le confort moderne).

Le problème c’est que cette instance est partagée au travers de tout le contexte applicatif. Donc pour toutes les requêtes en cours. Ça peut poser des problèmes si vous utilisez des transactions mais surtout c’est un goulot d’étranglement potentiel. En effet l’entity framework pour SQL est une UPA (Unité de persistance applicative) qui ne fait pas ou peu de pooling pour les connections à la base de données.

Une seule instance = connexions limitées = attentes du thread.

Instanciation à l’appel

Dans ce cas de figure le contexte est instancié à chaque fois qu’on en a besoin et détruit aussi tôt. Si l’on dispose d’une couche de service, il est instancié dans chacune des méthodes de service. Il est généralement utilisé dans un bloc using () {}.

L’avantage c’est que l’application travaille sur plusieurs instances de contexte, et qu’elle peut monter en charge bien plus facilement.

L’inconvénient c’est qu’on travaille avec des entités déconnectées du contexte, il faut donc penser à les rattacher entre deux contextes. Il faut aussi penser à demander explicitement le chargement des propriétés de navigation (avec la méthode .Include). Ça complique nettement plus les choses.

Instanciation à la requête

La solution que mon collègue Brian Knoepfel et moi avons trouvée est la suivante : instancier un seul contexte pour toute la durée de la requête. On profite ainsi du meilleur des deux mondes : simplicité, performances et flexibilité.

Dans le Init on enregistre un évènement qui sera levé à chaque fois qu’une requête est traitée.

A chaque fois qu’une requête se présente, on instancie un nouveau contexte entity framework qu’on stocke dans le contexte http (unique à chaque requête et stocké en mémoire).

Lorsque l’on a besoin de travailler avec le contexte un helper nous aide à y accéder.

Dans l’évènement de fin de requête, on dispose le contexte pour éviter fuites de mémoires et autres.

Voilà j’espère que cette solution vous aidera. Et n’hésitez pas à commenter si vous en avez une meilleure.

public class Global : System.Web.HttpApplication
{
    public override void Init()
    {
        EndRequest += Global_EndRequest;
        base.Init();
    }

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        HttpContext.Current.Items["EntityFrameworkContext"] = new AppEntitiesContext();
    }

    protected void Global_EndRequest(object sender, EventArgs e)
    {
        BLL.ContextHelper.RequestContext.Dispose();
    }

    public class ContextHelper
    {
        public static AppEntitiesContext RequestContext
        {
            get
            {
            return (AppEntitiesContext)HttpContext.Current.Items["EntityFrameworkContext"];
            }
        }
    }
}

Edité la dernière fois le 27 May 2021 par Vincent Biret


Tags: