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

Kal*_*exx 30 c# entity-framework ef-code-first entity-framework-4.1

看看我的Elmah错误日志,我看到InvalidOperationException来自Entity Framework 的一些s处理:

The context cannot be used while the model is being created.
Run Code Online (Sandbox Code Playgroud)

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

container.Register(Component.For<IUnitOfWork>()
                            .ImplementedBy<EFUnitOfWork>()
                            .LifeStyle
                            .PerWebRequest);
Run Code Online (Sandbox Code Playgroud)

我能够通过在VS中点击F5来重新创建错误以启动调试会话,并且当IIS正在启动时,将第二个网页加载到调试会话.

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

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


编辑:我更新了我的windsor控制器,现在包含以下代码:

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

但是,当我在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)
Run Code Online (Sandbox Code Playgroud)

Kal*_*exx 17

我终于找到了这个的真正原因,至少对我而言.

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

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


编辑:为了响应DotNetWise,我通过强制我的自定义成员资格提供者通过在我的构造函数中存储Windsor连接工厂来始终使用来自Windsor的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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

这个想法是成员资格提供程序执行的任何操作都UnitOfWork从Windsor 获取类,并使用它来执行操作(在这种情况下,我的UnitOfWork类是一个存储库持有者来包装我的EF数据上下文)

  • 对不起,我应该把它包括在内.我通过使我的一个成员资格提供程序方法向Windsor请求我的DbContext的新请求来解决它,并且从未在该方法之外使用相同的DBContext.在此之前,我通过我的会员提供者的构造函数请求DbContext并存储它以便以后保存,这导致了问题. (3认同)

Die*_*hon 7

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

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

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

我可能会补充一点,我认为这可以作为EF中的错误:它们应该使用线程安全(带锁或其他)代码进行DbContext初始化.

当然,更好的解决方案是NHibernate所做的:它SessionFactory是一个明确创建的独立对象Session.