2016-09-20 3 views
2

나는 며칠 동안 다음과 같은 일을하고있다.EntityFramework DbContext 수명주기 및 포스트그레스 : "작업이 이미 진행 중입니다."

난 저장소 패턴과 UnitOfWork 및 Postgres를 사용하여 EntityFramework와 함께 Mono에서 실행중인 Nancy 앱을 보유하고 있습니다. Nancy는 TinyIoC를 IoC 컨테이너로 사용합니다.

프런트 엔드에서 요청을 대기열에 두어 나중에 백엔드가 한 번에 한 요청을 받게되는 웹 앱이 있습니다. 이 모든 것이 잘 작동합니다.

그러나 동일한 백엔드에 연결하고 백엔드에 요청을 대기열에 넣지 않고 때때로 거의 동시에 요청을 실행하는 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 (또는 기본 연결)를 사용하여 상황을 폭파하게 만듭니다.

낸시 부트 스트 래퍼의 ConfigureRequestContainer 메소드에 종속성을 등록하려고 시도했지만 '연결이 열려 있지 않습니다.'예외가 발생합니다.

이 문제 뒤에 이론은이 문서에서 명확하게 설명되어 있습니다 : http://mehdi.me/ambient-dbcontext-in-ef6/

다음은 나에게 명확하지 않다 :

  • 이 멀티 스레딩 문제가 가정에서 내가 수정 있습니까?
  • 각각의 요청이 자신의 DbContext/connection을 사용하도록 확실히해야합니다. 따라서 물건이 충돌하지 않으며, TinyIoC/Nancy를 사용하여 DbContext의 수명주기를 관리해야합니다.

나는 이것이 복잡한 문제임을 이해합니다. 추가 정보가 필요한 경우 알려 주시기 바랍니다.

감사합니다.

+1

그렇습니다. 대부분 단일 DbContext가 여러 스레드에서 사용됩니다. 요청에 따라 새 인스턴스로 컨테이너에 DbContext를 등록하십시오. 그러면 인스턴스를 확인할 때마다 새 인스턴스가 만들어집니다. – Evk

+0

DbContext를 다음과 같이 등록합니다 :'container.Register ();'TinyIoC 문서를 올바르게 이해하면 DbContext와 다중 인스턴스를 등록하고 해결할 때마다 새로운 인스턴스를 생성해야합니다. 이것은 내가 지금 그것을 어떻게 설정했는지와 위의 상황을 일으키는 원인이 무엇인지에 대한 것입니다. 즉, 다중 인스턴스 임에도 불구하고 모든 요청에 ​​대해 새로운 인스턴스가 보장되지는 않습니다. – Corstiaan

+1

아니요 기본적으로 인터페이스를 싱글 톤으로 등록하므로 매번 동일한 인스턴스가 반환되므로 문제가 발생합니다. 컨테이너를 수행하십시오. ()을 등록하십시오. AsMultiInstance()를 사용하여 다중 인스턴스로 등록하십시오. – Evk

답변

0

같은 오류가있을 수있는 사람들의 향후 참조를 위해 내 의견을 약간 확장 할 예정입니다. 이미 알고있는 것처럼 Entity Framework의 DbContext은 "작업 단위"패턴을 따릅니다. 즉, 하나의 논리적 인 작업 (단위)에 하나의 인스턴스를 사용해야한다는 의미입니다. 여러 작업 단위에 대해 동일한 인스턴스를 재사용하는 것은 바람직하지 않으며, 일부 경우에는 실패로 이어질 수도 있습니다. SQL Server와 달리 PostgreSQL은 MARS (Multiple Active Result Sets)를 지원하지 않기 때문에 동일한 연결에서 동시에 여러 명령을 실행할 수 없습니다. DbContext의 단일 인스턴스를 여러 스레드에서 재사용 할 때 명령을 실행할 때 동일한 기본 sql 연결을 다시 사용하기 때문에 위의 오류가 발생합니다.

의견에서 언급했듯이 문제를 해결하는 방법은 항상 모든 작업에 대해 DbContext의 새 인스턴스를 만들고 나중에 삭제하는 것입니다.이 의미

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

로 등록 그리고 당신은 (예를 들어, 다른 클래스의 정적 필드 \ 싱글 인스턴스에 DbConext 인스턴스를 저장하지 않도록 당신의 ReflectUnitOfWork는 싱글, 당신은이 필드에 DbContext를 저장하는 경우 - 같은 문제를 다시).

관련 문제