当拦截的命令在事务内部时,用户如何使用 EF 命令拦截器将数据保存到数据库

时间:2021-07-09 10:22:23

标签: entity-framework transactions interceptor

我正在使用 EF 命令拦截器在命令执行后将一些数据保存到数据库。 (我有一些过滤器只在我想要的命令上运行它,所以它不会变成无限循环)

    class EFCommandInterceptor : IDbCommandInterceptor
    {
        public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
            var log = new LogModel
            {
                DateTime = DateTime.Now,
                Log = "Log"
            };

            using (var context = new ApplicationDbContext())
            {
                context.Logs.Add(log);
                var res = context.SaveChangesAsync().Result;
            }
        }
    }

这在大多数情况下都可以正常工作。但是如果我有任何需要记录在事务中的命令

using (TransactionScope transaction = new TransactionScope())
{
    // Some commands to intercept
}

我收到一个错误

<块引用>

var res = context.SaveChangesAsync().Result;

Win32Exception: The wait operation timed out

我已经尝试延长连接超时和命令超时计时器,但比抛出相同错误需要更长的时间。

我也试过在拦截器中创建一个新的 sql 连接和一个命令,但得到同样的错误。

EF Command Interceptor 拦截来自事务内部的命令时,有没有办法将数据保存到数据库中?

如果没有,拦截器有没有办法忽略事务内部的命令?

1 个答案:

答案 0 :(得分:0)

一般来说,最好使用单独的日志记录框架进行日志记录,例如 log4net、NLog 或 Serilog,因为这些记录器会在自己的线程中进行日志记录,并且应该对环境事务不敏感。

在 .Net core 5 下使用 Serilog 进行的快速测试表明,即使在 TransactionScope 内调用时,它也会记录日志,启用异步流,即回滚:

using Serilog;
using Serilog.Events;
using Serilog.Sinks.MSSqlServer;
using System.Transactions;

using var ts = new TransactionScope(asyncFlowOption: TransactionScopeAsyncFlowOption.Enabled);

var logger = new LoggerConfiguration()
    .WriteTo
    .MSSqlServer(
        connectionString: @"Server=.\sqlserver;Database=Test;Integrated Security=SSPI;",
        sinkOptions: new MSSqlServerSinkOptions { TableName = "Logs" })
    .CreateLogger();
logger.Information("info");

ts.Dispose(); // Rolls back. Added for clarity. Dispose happens anyway.

每次运行都会整齐地将日志记录写入测试数据库。

此处,Logs 是 Serilog 在未配置其他任何内容时所期望的标准日志记录表:

CREATE TABLE [Logs] (

   [Id] int IDENTITY(1,1) NOT NULL,
   [Message] nvarchar(max) NULL,
   [MessageTemplate] nvarchar(max) NULL,
   [Level] nvarchar(128) NULL,
   [TimeStamp] datetime NOT NULL,
   [Exception] nvarchar(max) NULL,
   [Properties] nvarchar(max) NULL

   CONSTRAINT [PK_Logs] PRIMARY KEY CLUSTERED ([Id] ASC) 
);

请注意,Serilog 建议使用静态记录器对象。