2016-09-20 72 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在現場有 - 同樣的問題再次)。