EntityFramework DbContext生命周期+ Postgres:“一项操作已在进行中。”

我几天来一直在弄乱以下几天。

我有一个在Mono上运行的Nancy应用程序,带有Repository模式和UnitOfWork的EntityFramework,以及Postgres。 Nancy使用TinyIoC作为IoC容器。

我有一个Web应用程序,它在前端对请求进行排队,因此后端每次都会遇到一个请求。 一切正常。

但是,当我运行连接到同一后端的iOS应用程序并且没有将请求排队到后端时,麻烦就开始了,有时几乎同时触发请求。

随机间隔后端开始抛出此错误:

2016-09-20T13:30:16.120057436Z app[web.1]: System.Data.Entity.Core.EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details. ---> System.InvalidOperationException: An operation is already in progress. 2016-09-20T13:30:16.120104535Z app[web.1]: at Npgsql.NpgsqlConnector.StartUserAction (ConnectorState newState)  in :0 2016-09-20T13:30:16.120113254Z app[web.1]: at Npgsql.NpgsqlCommand.ExecuteDbDataReaderInternal (CommandBehavior behavior)  in :0 2016-09-20T13:30:16.120119308Z app[web.1]: at Npgsql.NpgsqlCommand.ExecuteDbDataReader (CommandBehavior behavior)  in :0 2016-09-20T13:30:16.120125313Z app[web.1]: at System.Data.Common.DbCommand.ExecuteReader (CommandBehavior behavior)  in :0 2016-09-20T13:30:16.120131185Z app[web.1]: at (wrapper remoting-invoke-with-check) System.Data.Common.DbCommand:ExecuteReader (System.Data.CommandBehavior) 2016-09-20T13:30:16.120206045Z app[web.1]: at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.b__c (System.Data.Common.DbCommand t, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext`1 c)  in :0 2016-09-20T13:30:16.120220450Z app[web.1]: at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1[TInterceptor].Dispatch[TTarget,TInterceptionContext,TResult] (System.Data.Entity.Infrastructure.Interception.TTarget target, System.Func`3 operation, System.Data.Entity.Infrastructure.Interception.TInterceptionContext interceptionContext, System.Action`3 executing, System.Action`3 executed)  in :0 2016-09-20T13:30:16.120232740Z app[web.1]: at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.Reader (System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext)  in :0 2016-09-20T13:30:16.120267802Z app[web.1]: at System.Data.Entity.Internal.InterceptableDbCommand.ExecuteDbDataReader (CommandBehavior behavior)  in :0 2016-09-20T13:30:16.120274613Z app[web.1]: at System.Data.Common.DbCommand.ExecuteReader (CommandBehavior behavior)  in :0 2016-09-20T13:30:16.120318116Z app[web.1]: at (wrapper remoting-invoke-with-check) System.Data.Common.DbCommand:ExecuteReader (System.Data.CommandBehavior) 2016-09-20T13:30:16.120326788Z app[web.1]: at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition.ExecuteStoreCommands (System.Data.Entity.Core.EntityClient.EntityCommand entityCommand, CommandBehavior behavior)  in :0 2016-09-20T13:30:16.120332587Z app[web.1]: --- End of inner exception stack trace --- 2016-09-20T13:30:16.120336995Z app[web.1]: at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition.ExecuteStoreCommands (System.Data.Entity.Core.EntityClient.EntityCommand entityCommand, CommandBehavior behavior)  in :0 2016-09-20T13:30:16.120344218Z app[web.1]: at System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlan.Execute[TResultType] (System.Data.Entity.Core.Objects.ObjectContext context, System.Data.Entity.Core.Objects.ObjectParameterCollection parameterValues)  in :0 

我正在Nancy bootstrapper中注册依赖项:

 protected override void ConfigureApplicationContainer (TinyIoCContainer container) { base.ConfigureApplicationContainer (container); Database.SetInitializer (new NullDatabaseInitializer ()); // add this to allow prevent "The context cannot be used while the model is being created" container.Register (); container.Register ().AsSingleton (); container.Register (); container.Register<IUserRepository, ReflectUserRepository> (); container.Register (); container.Register (); container.Register<IRepositoryV2, EntityFrameworkRepository> (); container.Register<IAuthenticationUnitOfWork, ReflectUnitOfWork> (); container.Register<IRepository, NullRepository> (); //TODO remove this when port is complete container.Register (); container.Register (); container.Register<IRepository, ServiceStackOrmLiteRepository> (); container.Register (); container.Register (); container.Register<IUserManager, UserManager> (); container.Register<IUserMessageManager, UserMessageManager> (); etc... } 

我有一种感觉,这是一个multithreading问题,并且两个单独的请求正在使用相同的DbContext(或底层连接),这会导致事情爆发。

我已经尝试在Nancy引导程序的ConfigureRequestContainer方法中注册依赖项,但这会抛出“Connection is not open”exception。

本文清楚地解释了这个问题背后的理论: http : //mehdi.me/ambient-dbcontext-in-ef6/

我不清楚以下内容:

  • 假设这是一个multithreading问题,我是否正确?
  • 我需要知道确保每个请求使用它自己的DbContext /连接的正确方法,这样东西不会发生冲突,最好使用TinyIoC / Nancy来管理DbContext的生命周期。

我知道这是一个复杂的问题。 如果您需要任何其他信息,请与我们联系。

谢谢 :-)。

稍微扩展我的评论,以便将来可能有同样错误的人参考。 您可能已经知道,Entity Framework的DbContext遵循所谓的“工作单元”模式,这意味着您必须将一个实例用于一个逻辑部分(单元)工作。 不希望在多个工作单元中重复使用相同的实例,并且在某些情况下,这样甚至可能导致失败。 与SQL Server不同,Postgresql不支持MARS(多个活动结果集),这意味着它不支持同时在同一连接上执行多个命令。 当您从多个线程重用单个DbContext实例时,它们会在执行命令时重用相同的底层SQL连接,从而导致上述错误。

如评论中所述,解决问题的方法始终是为每个操作创建DbContext新实例,然后将其处理。 这意味着将其注册为

 container.Register ().AsMultiInstance(); 

并确保您永远不会将DbConext实例存储在另一个类的静态字段\ singleton实例中(例如,您的ReflectUnitOfWork是单例,如果您在某个字段中存储DbContextDbContext再次出现同样的问题)。