En regardant mes journaux d'erreurs Elmah, je vois quelques InvalidOperationException
s 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?
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
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)
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.
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)
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
.
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