2016-09-20 71 views
2

我已经搞了几天以下了。EntityFramework DbContext生命周期+ Postgres:“操作已在进行中。”

我有一个Nancy应用程序在Mono上运行,EntityFramework有存储库模式和UnitOfWork以及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) <0x41ad0150 + 0x00313> in <filename unknown>:0 
2016-09-20T13:30:16.120113254Z app[web.1]: at Npgsql.NpgsqlCommand.ExecuteDbDataReaderInternal (CommandBehavior behavior) <0x41acfe30 + 0x0002f> in <filename unknown>:0 
2016-09-20T13:30:16.120119308Z app[web.1]: at Npgsql.NpgsqlCommand.ExecuteDbDataReader (CommandBehavior behavior) <0x41acfe00 + 0x00013> in <filename unknown>:0 
2016-09-20T13:30:16.120125313Z app[web.1]: at System.Data.Common.DbCommand.ExecuteReader (CommandBehavior behavior) <0x41f1a3c0 + 0x00018> in <filename unknown>: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.<Reader>b__c (System.Data.Common.DbCommand t, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext`1 c) <0x41f1ac20 + 0x00027> in <filename unknown>: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) <0x41b1d3c0 + 0x0010e> in <filename unknown>: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) <0x41f1a880 + 0x00263> in <filename unknown>:0 
2016-09-20T13:30:16.120267802Z app[web.1]: at System.Data.Entity.Internal.InterceptableDbCommand.ExecuteDbDataReader (CommandBehavior behavior) <0x41f1a3f0 + 0x000e6> in <filename unknown>:0 
2016-09-20T13:30:16.120274613Z app[web.1]: at System.Data.Common.DbCommand.ExecuteReader (CommandBehavior behavior) <0x41f1a3c0 + 0x00018> in <filename unknown>: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) <0x41f154c0 + 0x00043> in <filename unknown>: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) <0x41f154c0 + 0x000b3> in <filename unknown>: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) <0x41f11e50 + 0x000a4> in <filename unknown>:0 

我注册喜欢的依赖,在南希引导程序:

protected override void ConfigureApplicationContainer (TinyIoCContainer container) 
     { 
      base.ConfigureApplicationContainer (container); 

      Database.SetInitializer<ReflectDbContext> (new NullDatabaseInitializer<ReflectDbContext>()); // add this to allow prevent "The context cannot be used while the model is being created" 

     container.Register<IReflectDbContext, ReflectDbContext>(); 
     container.Register<ReflectUnitOfWork>().AsSingleton(); 

     container.Register<IReflectUserRepository, ReflectUserRepository>(); 
     container.Register<IUserRepository<ReflectUser>, ReflectUserRepository>(); 

     container.Register<IReviewRepository, ReviewRepository>(); 

     container.Register<IReviewSetupRepository, ReviewSetupRepository>(); 

     container.Register<IRepositoryV2<ReflectUserActivityItem>, EntityFrameworkRepository<ReflectUserActivityItem>>(); 

     container.Register<IAuthenticationUnitOfWork<ReflectUser, ReflectUserActivityItem>, ReflectUnitOfWork>(); 

     container.Register<IRepository<ReflectUserActivityItem>, NullRepository<ReflectUserActivityItem>>(); //TODO remove this when port is complete 

     container.Register<IErrorLogger, SimpleLogLogger>(); 
     container.Register<IGeoIpDataProvider, TelizeGeoIpDataProvider>(); 
     container.Register<IRepository<ReviewSetup>, ServiceStackOrmLiteRepository<ReviewSetup>>(); 
     container.Register<IEmailExporter, MailChimpUserEmailDataExporter>(); 
     container.Register<IMailer, SmtpMailer>(); 
     container.Register<IUserManager<ReflectUser>, UserManager<ReflectUser, ReflectUserActivityItem>>(); 
     container.Register<IUserMessageManager<ReflectUser>, UserMessageManager<ReflectUser>>(); 

etc... 

} 

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

我已经尝试注册Nancy引导程序的ConfigureRequestContainer方法中的依赖项,但是这会抛出'Connection is not open`异常。

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

以下是我不清楚:

  • 我是正确的假设这是一个多线程的问题?
  • 我需要知道确保每个请求都使用它自己的DbContext /连接的正确方法,所以东西不会相互碰撞,最好使用TinyIoC/Nancy来管理DbContext的生命周期。

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

谢谢:-)。

+1

是的,这很可能是单个DbContext被多个线程使用。在您的容器中将DbContext注册为每个请求的新实例,以便每次解析它时都会创建新实例。 – Evk

+0

我像这样注册了DbContext:'container.Register ();'如果我正确理解TinyIoC文档,应该注册DbContext和多实例,并在每次解析时产生一个新实例。这是我目前设置的方式以及导致上述情况的原因。所以这意味着即使它是多实例,这并不能保证每个请求都有新的实例吗? – Corstiaan

+1

不,它会在默认情况下将接口注册为单例,因此每次都返回相同的实例,这会导致您观察到的问题。做container.Register ()。AsMultiInstance()注册为多实例。 – Evk

回答

0

会有点扩大我的意见,以供未来可能有同样错误的人参考。正如您可能已经知道的那样,Entity Framework的DbContext遵循所谓的“工作单元”模式,这意味着您必须为一个逻辑块(工作单元)使用一个实例。不希望重复使用同一个实例用于多个工作单元,并且在某些情况下,这可能会导致失败。与SQL Server不同,Postgresql不支持MARS(多活动结果集),这意味着它不支持同时在同一个连接上执行多个命令。当您从多个线程中重用单个实例DbContext时,它们在执行其命令时重用相同的基础sql连接,这导致上述错误。

正如评论中所述,解决问题的方法总是为每个操作创建一个新实例DbContext,然后对其进行处置。这意味着注册为

container.Register<IReflectDbContext, ReflectDbContext>().AsMultiInstance(); 

,并确保(你永远不会存储DbConext比如在另一个类的静态字段\单一实例,例如您的ReflectUnitOfWork是单身,如果你存储DbContext在现场有 - 同样的问题再次)。