web-dev-qa-db-fra.com

Comment éviter les erreurs EF "Le contexte ne peut pas être utilisé pendant la création du modèle"?

En regardant mes journaux d'erreurs Elmah, je vois quelques InvalidOperationExceptions d'Entity Framework qui traitent de:

The context cannot be used while the model is being created.

C'est avec la dernière bibliothèque EF CodeFirst de Nuget. La seule information que j'ai pu trouver sur le net est qu'elle est causée par des contextes de données comme singletons, ce qui n'est certainement pas mon cas. Dans mon installateur à Windsor, mon unité EF de structure de travail est enregistrée auprès de:

container.Register(Component.For<IUnitOfWork>()
                            .ImplementedBy<EFUnitOfWork>()
                            .LifeStyle
                            .PerWebRequest);

Je suis capable de recréer l'erreur en appuyant sur F5 dans VS pour démarrer une session de débogage, et pendant que IIS tourne, chargez une deuxième page Web vers la session de débogage.

Je soupçonne que c'est parce que l'utilisateur essaie d'accéder au système pendant que Asp.net a déchargé en raison du manque d'activité, ce qui est logique car mon produit est actuellement dans un test bêta très très petit. Cependant, comme de vraies personnes utilisent le site Web avec des données en direct, j'ai besoin du moins d'erreurs possible.

Quelqu'un a-t-il une idée de comment éviter que cela se produise?


Éditer:
        container.Register(Component.For<IUnitOfWork>().ImplementedBy<EFUnitOfWork>().LifeStyle.PerWebRequest);
        using (var context = new MyJobLeadsDbContext())
        {
            context.Set<UnitTestEntity>().Any();
        }

Cependant, lorsque j'essaie d'effectuer une 2e demande Web pendant que IIS charge l'application, l'erreur précédente se produit toujours


Modifier 2:
   at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
   at System.Data.Entity.Internal.InternalContext.Initialize()
   at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
   at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
   at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
   at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
   at System.Linq.Queryable.Where[TSource](IQueryable`1 source, Expression`1 predicate)
   at MyApp.DomainModel.Queries.Users.UserByEmailQuery.Execute() in C:\Users\KallDrexx\Documents\Projects\MyApp\MyApp.DomainModel\Queries\Users\UserByEmailQuery.cs:line 44
   at MyApp.Infrastructure.MyAppMembershipProvider.GetUser(String email, Boolean userIsOnline) in C:\Users\KallDrexx\Documents\Projects\MyApp\MyApp\Infrastructure\MyAppMembershipProvider.cs:line 102
   at System.Web.Security.Membership.GetUser(String username, Boolean userIsOnline)
   at System.Web.Security.Membership.GetUser()
   at MyApp.MyAppBaseController.Initialize(RequestContext requestContext) in C:\Users\KallDrexx\Documents\Projects\MyApp\MyApp\MyAppBaseController.cs:line 23
   at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
   at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
   at System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<>c__DisplayClassb.<BeginProcessRequest>b__5()
   at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass1.<MakeVoidDelegate>b__0()
   at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
   at System.Web.Mvc.MvcHandler.<>c__DisplayClasse.<EndProcessRequest>b__d()
   at System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f)
   at System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action)
   at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)
   at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
30
KallDrexx

J'ai finalement compris la véritable cause de cela, au moins pour moi.

Le problème était que je récupérais un DbContext de Windsor dans mon fournisseur d'adhésion Asp.Net personnalisé. Cela a provoqué un problème car le fournisseur d'appartenance a une durée de vie de l'ensemble de l'application, tandis que tous les autres appels de récupération pour le contexte db étaient de nouveaux contextes db pour les demandes Web spécifiques. Cela signifiait que deux contextes de base de données "tournaient" en même temps et donc cette erreur a été levée.

Cela a également causé beaucoup de problèmes de mise en cache d'entité difficiles à déboguer, donc toute personne qui utilise EF dans son fournisseur d'appartenance doit être très prudente quant à la durée de vie de son contexte.


Éditer

Par exemple:

public class CustomMembershipProvider : MembershipProvider
{
    private IServiceFactory _serviceFactory;

    public CustomMembershipProvider() : this(null) { }

    public CustomMembershipProvider(IServiceFactory factory)
    {
        // IF no factory was provided, we need to get one from the bootstrapper
        if (factory == null)
            _serviceFactory = new WindsorServiceFactory(Bootstrapper.WindsorContainer);
        else
            _serviceFactory = factory;
    }

    public override string ResetPassword(string email, string answer)
    {
        var unitOfWork = GetUnitOfWork();
        return new ResetUserPasswordCommand(unitOfWork).WithUserEmail(email).Execute();
    }

    private IUnitOfWork GetUnitOfWork()
    {
       return _serviceFactory.GetService<IUnitOfWork>();
    }
}

L'idée étant que toute action effectuée par le fournisseur d'appartenances obtient la classe UnitOfWork de Windsor et l'utilise pour effectuer l'action (dans ce cas, ma classe UnitOfWork est un détenteur de référentiel pour conclure mon EF contexte de données)

17
KallDrexx

J'ai rencontré le même problème dans une application WPF multithread.

Ma solution consistait à forcer l'initialisation de DbContext à partir du programme d'installation de Windsor:

container.Register(Component.For(TheDbContext.Blah.Blah));
using (var context = new TheDbContext())
      context.Set<SomeRandomEntity>().Any();

Je pourrais ajouter à mon avis que cela se qualifie comme un bug dans EF: ils auraient dû utiliser du code thread-safe (avec des verrous, ou autre) pour l'initialisation de DbContext.

Bien sûr, une meilleure solution est ce que fait NHibernate: le SessionFactory est un objet séparé créé explicitement du Session.

7
Diego Mijelshon

Lorsque j'ai rencontré ce problème, j'ai trouvé que c'était une connexion db qui avait mal tourné.

J'ai corrigé ma chaîne de connexion DB EntityFramework et tout allait bien

3
Oladipo Olasemo