c# - EntityFramework DbContext 生命周期 + Postgres : "An operation is already in progress."

标签 c# entity-framework nancy npgsql tinyioc

这几天我一直在弄乱以下内容。

我有一个在 Mono 上运行的 Nancy 应用程序,使用带有存储库模式和 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) <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

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

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 Bootstrap 的 ConfigureRequestContainer 方法中注册依赖项,但这会引发“连接未打开”异常。

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

我不清楚以下内容:

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

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

谢谢 :-).

最佳答案

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

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

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

并确保您永远不会将 DbConext 实例存储在另一个类的静态字段\单例实例中(例如,您的 ReflectUnitOfWork 是单例,如果您存储 DbContext 在一个字段中 - 又是同样的问题)。

关于c# - EntityFramework DbContext 生命周期 + Postgres : "An operation is already in progress.",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39595968/

相关文章:

c# - 具有 Azure SQL 数据库的 Entity Framework 返回 0 行

.net - 添加迁移给出文件名已经存在

nancy - 访问模块外部的 NancyContext

c# - 我的服务器/监听器缺少“真正的毅力”

c# - 更正 VB 的 CTYPE() 的 C# 转换对应物

.net - EF 4.3.1 迁移异常 - AlterColumn defaultValueSql 为不同的表创建相同的默认约束名称

c# - Nancy - 两个模块监听不同的端口

c# - 如何为路由实现另一个 var 参数名称

c# - 序列化类缺失数据

asp.net-mvc - 为自托管 NancyFx 复制 Razor View ?