如何使用 Audit.net 使用实体框架数据提供程序使用自定义 SQL 数据提供程序

如何解决如何使用 Audit.net 使用实体框架数据提供程序使用自定义 SQL 数据提供程序

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

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","PrimaryKey":{"Id":4},"OriginalValue":"test string updated 6","NewValue":"test string updated 7"}],"ColumnValues":{"Id":4,"Message":"test string updated 7"},"ValidationResults":[]}],"Result":2,"Success":true}}

现在我的问题是,我同时更新了两条记录。 Audit.NET 已将两者作为单个记录记录在审计表中。有没有办法分别为这两个更新插入日志详细信息。

解决方法

你至少有两个选择

使用实体框架数据提供程序

如果您可以将审计日志表映射到实体框架 DbContext(它可以是您正在审计的同一个 DbContext 或不同的 DbContext),那么您可以使用 EntityFramework Data Provider 而不是 SQL 数据提供程序。

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

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 表的记录中:

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("...")));

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?