如何使用 Audit.net 将 Update<T>(IList<T>) 操作记录为多个审计记录

How to log Update<T>(IList<T>) action as multiple audit records using Audit.net

我必须更新 table 中的批量记录。我是这样做的。

BaseRepository.cs

    public IList<T> Update<T>(IList<T> instance) where T : class
    {
        IList<T> insertedItems = new List<T>();
        int totalCount = instance.Count;
        int count = 0;

        foreach (var item in instance)
        {
            insertedItems.Add(Update(item, count == (totalCount - 1)));
            count++;
        }

        try
        {
            context.SaveChanges();
        }
        catch (DbEntityValidationException ex)
        {
            //HandleDbEntityValidationException(ex);
        }
        //
        return insertedItems;
    }

测试方法

    public bool TestUpdate()
    {
        try
        {
            List<Test> list = new List<Test>();
            Test test1 = new Test();
            test1.Id = 3;
            test1.Message = "test string updated 40";
            Test test2 = new Test();
            test2.Id = 4;
            list.Add(test1); list.Add(test2);
            test2.Message = "test string updated 7";
            _repository.Update<Test>(list);
            this.unitOfWork.Save();
        }
        catch (Exception ex)
        {

        }
        finally
        {
            this.unitOfWork.Dispose();
        }
        return true;
    }

然后想用Audit.net做日志审计。我做了如下...

Global.asax.cs

    Audit.Core.Configuration.DataProvider = new SqlDataProvider()
        {
            ConnectionString = "Data Source=FTD-NB-MADHARA;Initial Catalog=TestAuditTrail;User ID=sctavp_user;Password=welcome@123;MultipleActiveResultSets=true",
            Schema = "dbo",
            TableName = "Event",
            IdColumnName = "EventId",
            JsonColumnName = "JsonData",

            CustomColumns = new List<CustomColumn>()
        {
            new CustomColumn("UserName", ev=> ev.Environment.UserName.ToString()),
            new CustomColumn("MachineName", ev=> ev.Environment.MachineName.ToString()),
            new CustomColumn("DomainName", ev=> ev.Environment.DomainName.ToString()),
            new CustomColumn("ModuleName", ev => "AuditTrail"),
            new CustomColumn("CallingMethod", ev=> ev.Environment.CallingMethodName.ToString()),
            new CustomColumn("DatabaseName", ev=> ev.GetEntityFrameworkEvent().Database.ToString()),
            new CustomColumn("SchemaName", ev=> ev.GetEntityFrameworkEvent().Entries[0].Schema.ToString()),
            new CustomColumn("TableName", ev=> ev.GetEntityFrameworkEvent().Entries[0].Table.ToString()),
            new CustomColumn("Action", ev=> ev.GetEntityFrameworkEvent().Entries[0].Action.ToString()),}};

上下文变化

    private static DbContextHelper _helper = new DbContextHelper();
    private readonly IAuditDbContext _auditContext;

    public AuditTrailContext() : base("ApplicationDatabase")
    {
        //AuditDataProvider = new NullDataProvider();
        _auditContext = new DefaultAuditContext(this);
        _helper.SetConfig(_auditContext);
        
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Add(new DecimalPrecisionAttributeConvention());
    }

    #region without AuditDbContext
    public override int SaveChanges()
    {
        return _helper.SaveChanges(_auditContext, () => base.SaveChanges());
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        return await _helper.SaveChangesAsync(_auditContext, () => base.SaveChangesAsync(cancellationToken));
    }

结果

JsonData 字段

    {"EventType":"DefaultAuditContext","Environment":{"UserName":"MadharaU","MachineName":"FTD-NB-MADHARA","DomainName":"BRANDIXLK","CallingMethodName":"Rocky.AuditService.Data.EntityManager.BaseRepository.Update()","AssemblyName":"Rocky.AuditService.Data.EntityManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null","Culture":"en-US"},"StartDate":"2021-02-23T03:52:47.0005326Z","EndDate":"2021-02-23T03:52:47.0371697Z","Duration":37,"EntityFrameworkEvent":{"Database":"AuditTrail","Entries":[{"Schema":"dbo","Table":"Test","Action":"Update","PrimaryKey":{"Id":3},"Changes":[{"ColumnName":"Message","OriginalValue":"test string updated 39","NewValue":"test string updated 40"}],"ColumnValues":{"Id":3,"Message":"test string updated 40"},"Valid":true,"ValidationResults":[]},{"Schema":"dbo","Table":"Test","Action":"Update","PrimaryKey":{"Id":4},"Changes":[{"ColumnName":"Message","OriginalValue":"test string updated 6","NewValue":"test string updated 7"}],"ColumnValues":{"Id":4,"Message":"test string updated 7"},"Valid":true,"ValidationResults":[]}],"Result":2,"Success":true}}

现在我的问题是,我同时更新了两条记录。 Audit.NET 在审计 table 中将两者记录为一条记录。有什么方法可以分别为这两个更新插入日志详细信息。

您至少有两个选择

使用 Entity Framework 数据提供程序

如果您可以将审核日志 table 映射到 Entity Framework DbContext(它可以是您正在审核的相同 DbContext 或不同的 DbContext),那么您可以使用 EntityFramework Data Provider 而不是 SQL 数据提供程序。

例如,假设您有一个 AuditLog table 映射到 DbContext:

public class AuditLog
{
    public int Id { get; set; }
    public DateTime Date { get; set; }
    public string User { get; set; }
    public string Table { get; set; }
    public string JsonData { get; set; }
}
public class LogsDbContext : DbContext
{
    public DbSet<AuditLog> AuditLogs { get; set; }
    //...
}

然后您可以设置 EF 数据提供程序以将每个已审核的条目记录到 AuditLog 上的记录中 table:

Audit.Core.Configuration.Setup()
    .UseEntityFramework(config => config
        .UseDbContext<LogsDbContext>()
        .AuditTypeMapper(_ => typeof(AuditLog))
        .AuditEntityAction<AuditLog>((ev, entry, auditLog) =>
        {
            auditLog.Date = DateTime.UtcNow;
            auditLog.Table = entry.Table;
            auditLog.User = ev.Environment.UserName;
            auditLog.JsonData = entry.ToJson();
        })
        .IgnoreMatchedProperties(true));

使用自定义 SQL 数据提供程序

继承自SqlDataProvider,重写Insert/InsertAsync触发每个实体条目的保存:

public class SingleSqlProvider : SqlDataProvider
{
    public SingleSqlProvider(Action<ISqlServerProviderConfigurator> config) : base(config) { }

    public override object InsertEvent(AuditEvent auditEvent)
    {
        var efEvent = auditEvent as AuditEventEntityFramework;
        object lastId = null;
        if (efEvent != null)
        {
            foreach (var entry in efEvent.EntityFrameworkEvent.Entries)
            {
                var clone = AuditEvent.FromJson<AuditEventEntityFramework>(auditEvent.ToJson());
                clone.EntityFrameworkEvent.Entries.Clear();
                clone.EntityFrameworkEvent.Entries.Add(entry);
                lastId = base.InsertEvent(clone);
            }
        }
        else
        {
            return base.InsertEvent(auditEvent);
        }
        return lastId;
    }

    public async override Task<object> InsertEventAsync(AuditEvent auditEvent)
    {
        // same but Async...
    }
}

那么设置可以是:

Audit.Core.Configuration.Setup()
    .UseCustomProvider(new SingleSqlProvider(config => config
        .ConnectionString("...")
        .TableName("...")));