【问题标题】:NInject and multi-threading issueNInject 和多线程问题
【发布时间】:2012-12-26 13:19:45
【问题描述】:

我们将 Entity framework 4 用于我们的 Web 应用程序并使用 Ninject 作为我们的 DI 容器。我们有一个日志表来记录我们应用程序中发生的所有事情,而且我们还有后端 WCF 工作流服务,它再次将日志写入日志表。

对于来自网络的所有请求,我们使用

StandardScopeCallbacks.Request(ctx);

对于后端的所有内容,我们使用

StandardScopeCallbacks.ThreadScope(ctx);

我们的记录方法很简单

 public void Log(string message, LogLevel level)
        {
             var log = new ApplicationLog()
                    {
                        LogDate = DateTime.Now,
                        Message = message,
                        LogLevel = Enum.GetName(typeof(LogLevel), level)
                    };

                    db.ApplicationLog.Add(log);
                    db.SaveChanges();          
        }

问题: 如果我们有更多的线程在运行,那么我们会得到一个异常

System.Data.SqlClient.SqlException:不允许新事务 因为会话中还有其他线程在运行。在

System.Data.SqlClient.SqlConnection.OnError(SqlException 异常, 布尔中断连接)在 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning() 在 System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler、SqlDataReader 数据流、 BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) 在 System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(字节 [] 缓冲区,TransactionManagerRequestType 请求,字符串 transactionName, TransactionManagerIsolationLevel isoLevel,Int32 超时, SqlInternalTransaction 事务,TdsParserStateObject stateObj, Boolean isDelegateControlRequest) 在

System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest transactionRequest,字符串 transactionName,IsolationLevel iso, SqlInternalTransaction 内部事务,布尔值 isDelegateControlRequest) 在

System.Data.SqlClient.SqlInternalConnection.BeginSqlTransaction(IsolationLevel iso,字符串事务名称)在 System.Data.SqlClient.SqlInternalConnection.BeginTransaction(隔离级别 异)在 System.Data.SqlClient.SqlConnection.BeginDbTransaction(隔离级别 隔离级别)在 System.Data.EntityClient.EntityConnection.BeginDbTransaction(隔离级别 隔离级别)

我知道我可以使用“使用”语句创建新上下文并编写 ti Log 表而不会出现任何问题。但我不想这样做,因为这会使一切变得比在线程范围中创建上下文更慢。

处理这种情况的最佳方法是什么?我需要执行时间最短的实现。

如果您需要更多信息,请发表评论。提前谢谢你。

【问题讨论】:

  • 变量db从何而来?它是否注入到您的 Log 方法所在的任何类的构造函数中? db 是你的 EF 上下文吗?
  • 我希望你很清楚每个主要的日志框架都有附加程序,它们直接写入你选择的数据库。为什么要重新发明轮子?还请提供更多背景信息,以便我们为您提供帮助
  • 您是否在 Log 方法中尝试过 lock(db){ ... }?
  • 感谢大家的cmets。我知道我们可以使用日志框架并摆脱所有问题。但是我们需要这个来进行中间修复。是的 db 是 EF 上下文并从 jInject 注入。
  • I can you lock(db){..} and using(var db = new DBContext()) {...}.. 但我仍然想使用为我创建的相同上下文线程使用 Ninject。

标签: c# multithreading entity-framework ninject


【解决方案1】:

你应该听听丹尼尔的评论。 NLog 和 Log4Net 是更好的选择。

我不建议像 Jalalx 提到的那样锁定 db,因为如果您有大量日志,它最终可能会减慢锁的执行速度。如果你没有大量写入日志,那么一定要试一试。

两个选项--

  1. 按线程注册您的数据库连接/上下文 -- 这样您就不必担心跨线程共享,它不会发生。
  2. 创建一个“DbConnectionFactory”,创建可以在每个线程/执行的上下文中调用的数据库连接/上下文。

所以基本上它要么让 Ninject 管理它,要么创建一个抽象以便你自己管理它。

顺便说一句,ninject 不是 .NET 中最慢的 DI 框架吗?

【讨论】:

  • 我不明白为什么 ninjects 速度是一个问题。如果有人使用 ORM 将日志写入数据库,我不会担心 DI 容器;)
  • @DanielMarbach 是的,这完全不是问题。我之所以提出它,是因为我发现大多数人都不知道,尤其是因为它被用于各种微软书籍中
  • 我同意@Micah 和 DanielMarbach 的观点。我们让 Ninject 为我们每个线程创建新的上下文。这个例子可能不仅仅是记录。它可能正在更新状态表或其他东西。例如,IIS 产生 16 个线程来加载相同的 Windows 工作流 16 次......其中一项活动是更新状态表......然后我有这个异常 System.Data.SqlClient.SqlException: New transaction is not allowed 因为还有其他会话中运行的线程。
  • 听起来你并没有真正保持每个线程的数据库连接。我建议您将 NLog 或 log4net 添加到您的项目中,而不是立即重做您的日志记录,而是这样您就可以在您的数据库代码/调用周围添加日志记录语句以监视实际发生的线程事件。 (它们都在输出中显示线程 ID)。这将有助于您进行诊断。
猜你喜欢
  • 1970-01-01
  • 2020-02-02
  • 2017-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-02
相关资源
最近更新 更多