Elmahのエラーログを見ると、以下を処理するEntity FrameworkのInvalidOperationException
sがいくつか見つかります。
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リクエストを実行しようとすると、以前のエラーが引き続き発生します
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)
少なくとも私にとっては、この本当の原因がようやくわかりました。
問題は、カスタム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をラップするリポジトリホルダーです)データコンテキスト)
マルチスレッドのWPFアプリで同じ問題が発生しました。
私の回避策は、WindsorインストーラーからDbContextの初期化を強制することでした。
container.Register(Component.For(TheDbContext.Blah.Blah));
using (var context = new TheDbContext())
context.Set<SomeRandomEntity>().Any();
私の意見ではこれはEFのバグと見なされます。DbContextの初期化にはスレッドセーフ(ロックなど)のコードを使用する必要がありました。
もちろん、より良い解決策はNHibernateが行うことです:SessionFactory
は明示的に作成されたSession
とは別のオブジェクトです。
この問題が発生したとき、dbconnectionが間違っていることがわかりました。
EntityFramework dbconnection文字列を修正したところ、すべて問題ありませんでした