Как регистрировать действие Update ‹T› (IList ‹T›) как несколько записей аудита с помощью Audit.net

Мне нужно обновить массовые записи в таблице. Я сделал это следующим образом.

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;
    }

TestMethod

    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()),}};

ContextChanges

    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 зарегистрировал обе как одну запись в таблице аудита. Есть ли способ отдельно вставить данные журнала для этих двух обновлений.


person Mad    schedule 25.02.2021    source источник


Ответы (1)


У вас есть как минимум два варианта

Используйте поставщик данных Entity Framework

Если вы можете сопоставить таблицу журнала аудита с Entity Framework DbContext (это может быть тот же самый DbContext, который вы проверяете, или другой), тогда вы можете использовать EntityFramework Data Provider вместо поставщика данных SQL.

Например, если у вас есть таблица AuditLog, сопоставленная с 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:

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("...")));
person thepirat000    schedule 25.02.2021
comment
Я использовал второй вариант, и он сработал !!! Спасибо за ответ и потраченное время. - person Mad; 01.03.2021