2016-09-20 8 views
2

Я уже несколько дней возился со следующим.EntityFramework Жизненный цикл DbContext + Postgres: «Операция уже выполняется».

У меня есть приложение Nancy, работающее на Mono, с EntityFramework с шаблоном репозитория и UnitOfWork и Postgres. Нэнси использует 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... 

} 

У меня есть ощущение, что это мульти -threading и что два отдельных запроса используют один и тот же DbContext (или базовое соединение), из-за чего вещи могут взорваться.

Я уже пробовал зарегистрироваться в методе ConfigureRequestContainer загрузчика Nancy, но это исключает исключение «Connection is not open».

Теория позади этого вопроса четко изложена в этой статье: http://mehdi.me/ambient-dbcontext-in-ef6/

Следующая неясно мне:

  • Я правильно предполагая, что это многопоточность вопрос?
  • Мне нужно знать правильный способ убедиться, что каждый запрос использует собственный DbContext/connection, поэтому материал не сталкивается, используя TinyIoC/Nancy, чтобы управлять жизненным циклом DbContext.

Я понимаю, что это сложная проблема. Дайте мне знать, если вам нужна дополнительная информация.

Спасибо :-).

+1

Да, это, скорее всего, единственный DbContext используется несколькими потоками. Зарегистрируйте DbContext в своем контейнере как новый экземпляр для каждого запроса, чтобы каждый экземпляр создавался каждый раз, когда вы его разрешаете. – Evk

+0

Я регистрирую DbContext так: 'container.Register ();' Если я правильно понимаю документы TinyIoC, это должно регистрировать DbContext и multi-instance и давать новый экземпляр каждый раз, когда он разрешен. Вот как я это настроил в данный момент и что вызывает вышеупомянутую ситуацию. Таким образом, это означает, что, хотя это multi-instance, это не гарантирует новый экземпляр каждого запроса? – 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 в поле там - тот же проблема еще раз).

 Смежные вопросы

  • Нет связанных вопросов^_^