如何防止 EF“创建模型时无法使用上下文”错误
本文关键字:上下文 错误 EF 何防止 创建 模型 | 更新日期: 2023-09-27 17:57:16
查看我的Elmah错误日志,我看到实体框架中的一些InvalidOperationException
处理:
The context cannot be used while the model is being created.
这是来自 Nuget 的最新 EF CodeFirst 库。 我在网上能够找到的唯一信息是,它是由将数据上下文作为单例引起的,这肯定不是我的情况。 在我的温莎安装程序中,我的 EF 工作单元结构正在注册到:
container.Register(Component.For<IUnitOfWork>()
.ImplementedBy<EFUnitOfWork>()
.LifeStyle
.PerWebRequest);
我能够通过在VS中按F5来重新启动错误以启动调试会话,并且在IIS旋转时加载第二个网页到调试会话。
我怀疑这是因为用户在 Asp.net 由于缺乏活动而卸载时尝试访问系统,这是有道理的,因为我的产品目前处于非常非常小的 beta 测试中。 但是,由于真实的人正在使用带有实时数据的网站,因此我需要尽可能少地发生错误。
有没有人知道如何防止这种情况发生?
编辑:我更新了我的温莎控制器,现在包含以下代码:
container.Register(Component.For<IUnitOfWork>().ImplementedBy<EFUnitOfWork>().LifeStyle.PerWebRequest);
using (var context = new MyJobLeadsDbContext())
{
context.Set<UnitTestEntity>().Any();
}
但是,当我尝试在 IIS 加载应用程序时执行第二个 Web 请求时,仍然会出现以前的错误
编辑 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)
我终于弄清楚了这背后的真正原因,至少对我来说是这样。
问题是我正在我的自定义 Asp.Net 会员提供程序中从温莎检索DbContext
。 这导致了一个问题,因为成员资格提供程序具有整个应用程序的生命周期,而数据库上下文的所有其他检索调用都是特定 Web 请求的新数据库上下文。这意味着两个数据库上下文同时"旋转",因此抛出此错误。
这也导致了许多难以调试的实体缓存问题,因此在其成员资格提供程序中使用 EF 的任何人都需要非常小心其上下文生存期。
编辑:为了响应 DotNetWise,我通过强制我的自定义成员资格提供程序始终使用来自温莎的 EF 连接来解决此问题,方法是将温莎连接工厂存储在我的构造函数中,然后始终在那时从工厂检索我的 EF 数据上下文。
例如:
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>();
}
}
这个想法是,成员资格提供程序执行的任何操作都会从 Windsor 获取 UnitOfWork
类,并使用它来执行操作(在本例中,我的 UnitOfWork
类是包装我的 EF 数据上下文的存储库持有者)
我在多线程 WPF 应用程序中遇到了同样的问题。
我的解决方法是从温莎安装程序强制初始化 DbContext:
container.Register(Component.For(TheDbContext.Blah.Blah));
using (var context = new TheDbContext())
context.Set<SomeRandomEntity>().Any();
我可能会补充一点,在我看来,这符合 EF 中的一个错误:他们应该使用线程安全(带锁或其他什么)代码进行 DbContext 初始化。
当然,更好的解决方案是NHibernate所做的:SessionFactory
是显式创建的,与Session
分开的对象。
当我遇到这个问题时,我发现这是一个数据库连接出错。
我更正了我的实体框架数据库连接字符串,一切都很好