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

发布于 2024-11-09 05:20:57 字数 3669 浏览 0 评论 0原文

查看我的 Elmah 错误日志,我看到实体框架中的一些 InvalidOperationException 处理:

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

这是来自 Nuget 的最新 EF CodeFirst 库。我在网上找到的唯一信息是,它是由数据上下文作为单例引起的,这肯定不是我的情况。在我的 Windsor 安装程序中,我的 EF 工作单元结构正在注册:

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

我可以通过在 VS 中按 F5 启动调试会话来重新创建错误,并且在 IIS 旋转时将第二个网页加载到调试会话。

我怀疑这是因为用户正在尝试访问系统,而 Asp.net 由于缺乏活动而已卸载,这是有道理的,因为我的产品目前正在进行非常小的 Beta 测试。但是,由于真实的人正在使用带有实时数据的网站,因此我需要尽可能少地发生错误。

有谁知道如何防止这种情况发生?


Edit: I updated my windsor controller to now contain the following code:

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

但是,当我在 IIS 加载应用程序时尝试执行第二个 Web 请求时,仍然会出现之前的错误


Edit 2: As requested, here is the stack

   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)

Looking at my Elmah error logs, I am seeing a few InvalidOperationExceptions from Entity Framework that deal with:

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

This is with the latest EF CodeFirst library from Nuget. The only information I have been able to find on the net is that it is being caused by having data contexts as singletons, which is most certainly not my case. In my Windsor installer, my EF unit of work structure is being registered with:

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

I am able to recreate the error by hitting F5 in VS to start a debugging sessions, and while IIS is spinning up load up a second webpage to the debug session.

I suspect it is because the user is trying to access the system while Asp.net has unloaded due to the lack of activity, which makes sense as my product is currently in a very very small beta test. However, since real people are using the website with live data, I need as little errors occurring as possible.

Does anyone have any idea how to prevent this from occurring?


Edit: I updated my windsor controller to now contain the following code:

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

However, when I attempt to perform a 2nd web request while IIS is loading the application, the previous error still occurs


Edit 2: As requested, here is the stack

   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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

晨敛清荷 2024-11-16 05:20:57

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

问题是我正在自定义 Asp.Net 会员资格提供程序中从 Windsor 检索 DbContext 。这导致了一个问题,因为成员资格提供程序具有整个应用程序的生命周期,而数据库上下文的所有其他检索调用都是特定 Web 请求的新数据库上下文。这意味着两个数据库上下文同时“旋转”,因此引发了此错误。

这也导致了许多难以调试的实体缓存问题,因此在成员资格提供程序中使用 EF 的任何人都需要非常小心其上下文生命周期。


Edit: In response to DotNetWise, I solved this by forcing my custom membership provider to always use an EF connection from Windsor by storing the Windsor connection factory in my constructor, then always retrieving my EF data context from the factory at that point.

例如:

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 数据上下文)

I finally figured out the true cause of this, at least for me.

The issue was that I was retrieving a DbContext from Windsor in my custom Asp.Net Membership provider. This caused an issue because the membership provider has a lifespan of the whole application, while all other retrieval calls for the db context were new db contexts for the specific web requests. This meant that two database contexts were "spinning up" at the same time and thus this error was thrown.

This also caused a lot of hard to debug entity caching issues as well, so anyone who uses EF in their membership provider needs to be real careful about their context lifetime.


Edit: In response to DotNetWise, I solved this by forcing my custom membership provider to always use an EF connection from Windsor by storing the Windsor connection factory in my constructor, then always retrieving my EF data context from the factory at that point.

For example:

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>();
    }
}

The idea being that any action the membership provider performs gets the UnitOfWork class from Windsor, and uses that to perform the action (in this case my UnitOfWork class is a repository holder to wrap up my EF data context)

说不完的你爱 2024-11-16 05:20:57

我在多线程 WPF 应用程序中遇到了同样的问题。

我的解决方法是强制从 Windsor 安装程序初始化 DbContext:

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

在我看来,我可能会补充说这属于 EF 中的错误:他们应该使用线程安全(带锁或其他)代码来进行 DbContext 初始化。

当然,更好的解决方案是 NHibernate 所做的:SessionFactory 是一个显式创建的、独立于 Session 的对象。

I ran into the same issue in a multithreaded WPF app.

My workaround was to force DbContext initialization from the Windsor installer:

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

I might add in my opinion this qualifies as a bug in EF: they should have used thread-safe (with locks, or whatever) code for DbContext initialization.

Of course, a better solution is what NHibernate does: the SessionFactory is an explicitly created, separate object from the Session.

谁与争疯 2024-11-16 05:20:57

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

我更正了 EntityFramework dbconnection 字符串,一切都很好

When i encountered this issue, i found it was a dbconnection gone wrong.

I corrected my EntityFramework dbconnection string and all was fine

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文