web-dev-qa-db-ja.com

EF「モデルの作成中はコンテキストを使用できません」エラーを回避するにはどうすればよいですか?

Elmahのエラーログを見ると、以下を処理するEntity FrameworkのInvalidOperationExceptionsがいくつか見つかります。

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が2番目のWebページを起動してデバッグセッションにロードしている間に、エラーを再現できます。

これは、Asp.netがアクティビティの欠如のためにアンロードされている間にユーザーがシステムにアクセスしようとしているためだと思います。私の製品は現在、非常に小さなベータテスト中であるため、理にかなっています。ただし、実際の人はライブデータを使用してWebサイトを使用しているため、エラーをできるだけ発生させないようにする必要があります。

これを防ぐ方法はありますか?


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

ただし、IISがアプリケーションをロードしているときに2番目の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)
30
KallDrexx

少なくとも私にとっては、この本当の原因がようやくわかりました。

問題は、カスタムAsp.NetメンバーシッププロバイダーでWindsorからDbContextを取得していたことです。メンバーシッププロバイダーにはアプリケーション全体の寿命があるため、これにより問題が発生しました。dbコンテキストに対する他のすべての取得呼び出しは、特定のWeb要求に対する新しいdbコンテキストでした。これは、2つのデータベースコンテキストが同時に「スピンアップ」していたため、このエラーがスローされたことを意味します。

これにより、エンティティキャッシュの問題もデバッグするのが非常に困難になったため、メンバーシッププロバイダーで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をラップするリポジトリホルダーです)データコンテキスト)

17
KallDrexx

マルチスレッドのWPFアプリで同じ問題が発生しました。

私の回避策は、WindsorインストーラーからDbContextの初期化を強制することでした。

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

私の意見ではこれはEFのバグと見なされます。DbContextの初期化にはスレッドセーフ(ロックなど)のコードを使用する必要がありました。

もちろん、より良い解決策はNHibernateが行うことです:SessionFactoryは明示的に作成されたSessionとは別のオブジェクトです。

7
Diego Mijelshon

この問題が発生したとき、dbconnectionが間違っていることがわかりました。

EntityFramework dbconnection文字列を修正したところ、すべて問題ありませんでした

3
Oladipo Olasemo