Как я могу предотвратить ошибки EF «Контекст не может использоваться во время создания модели»? - PullRequest
30 голосов
/ 23 мая 2011

Глядя на мои журналы ошибок Elmah, я вижу несколько InvalidOperationException s из Entity Framework, которые имеют дело с:

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

Это с последней библиотекой EF CodeFirst от Nuget. Единственная информация, которую мне удалось найти в сети, заключается в том, что она вызвана наличием контекстов данных в виде синглетонов, что, безусловно, не в моем случае. В моем установщике Windsor моя единица рабочей структуры EF регистрируется по адресу:

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

Я могу воссоздать ошибку, нажав F5 в VS, чтобы запустить сеансы отладки, и пока IIS набирает обороты, загрузите вторую страницу в сеанс отладки.

Я подозреваю, что это потому, что пользователь пытается получить доступ к системе, когда Asp.net выгружен из-за отсутствия активности, что имеет смысл, так как мой продукт в настоящее время находится в очень очень маленьком бета-тестировании. Однако, поскольку реальные люди используют веб-сайт с оперативными данными, мне нужно как можно меньше ошибок.

Кто-нибудь знает, как предотвратить это?


Редактировать: Я обновил свой контроллер виндзор, чтобы теперь содержать следующий код:
        container.Register(Component.For<IUnitOfWork>().ImplementedBy<EFUnitOfWork>().LifeStyle.PerWebRequest);
        using (var context = new MyJobLeadsDbContext())
        {
            context.Set<UnitTestEntity>().Any();
        }

Однако, когда я пытаюсь выполнить 2-й веб-запрос во время загрузки приложения IIS, предыдущая ошибка по-прежнему возникает


Редактировать 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)

Ответы [ 3 ]

17 голосов
/ 02 ноября 2011

Я наконец выяснил истинную причину этого, по крайней мере, для себя.

Проблема заключалась в том, что я получал DbContext от Виндзора в моем пользовательском провайдере членства в Asp.Net. Это вызвало проблему, поскольку поставщик членства имеет срок службы всего приложения, в то время как все другие поисковые запросы для контекста db были новыми контекстами db для определенных веб-запросов. Это означало, что два контекста базы данных «раскручивались» одновременно, и, таким образом, эта ошибка была выброшена.

Это также вызвало много трудных для отладки проблем с кэшированием сущностей, поэтому любой, кто использует EF в своем поставщике членства, должен быть очень осторожным в отношении времени жизни своего контекста.


Редактировать : В ответ на DotNetWise я решил эту проблему, заставив моего настраиваемого поставщика членства всегда использовать подключение EF из Windsor, сохранив фабрику соединений Windsor в моем конструкторе, а затем всегда извлекать контекст данных 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>();
    }
}

Идея заключается в том, что любое действие, выполняемое провайдером членства, получает класс UnitOfWork от Windsor и использует его для выполнения действия (в этом случае мой класс UnitOfWork является держателем хранилища, чтобы обернуть мой контекст данных EF)

7 голосов
/ 23 мая 2011

Я столкнулся с той же проблемой в многопоточном приложении WPF.

Мой обходной путь - принудительная инициализация DbContext из установщика Windsor:

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

Я мог бы добавить, по моему мнению, это соответствуеткак ошибка в EF: они должны были использовать потокобезопасный (с блокировками или любой другой) код для инициализации DbContext.

Конечно, лучшее решение - это то, что делает NHibernate: SessionFactory явно создан, отделить объект от Session.

3 голосов
/ 03 мая 2013

Когда я столкнулся с этой проблемой, я обнаружил, что произошла ошибка соединения с db.

Я исправил строку EntityFramework dbconnection, и все было в порядке

...