如何防止 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)

如何防止 EF“创建模型时无法使用上下文”错误

我终于弄清楚了这背后的真正原因,至少对我来说是这样。

问题是我正在我的自定义 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分开的对象。

当我遇到这个问题时,我发现这是一个数据库连接出错。

我更正了我的实体框架数据库连接字符串,一切都很好