簡體   English   中英

Audit.NET Entity Framework Core - 相關實體管理

[英]Audit.NET Entity Framework Core - Related Entities management

我正在開發 ASP.NET Core 3.1 web 應用程序。 我想在數據庫中保存數據時添加審計跟蹤/日志。

我從這個 SO answer中獲得靈感,開始在測試項目中使用 Audit.NET。
這是我的目標(類似於相關的 SO 線程):

  1. 將審計記錄存儲在不同的數據庫中:使用額外的AppAuditDbContext幾乎完成
  2. 每個類型都有一個與審計類型匹配的審計表(帶有附加審計字段):通過對額外AppAuditDbContext的反射完成
  3. 不需要維護單獨的審計實體。 操作數據庫和審計數據庫之間的更改應該是無縫的:通過對審計實體的額外AppAuditDbContextDataAnnotations的反射來完成
  4. 檢索相關實體的審計數據: TO DO

此時,我可以 CRUD 一個獨立的審計實體,並在審計數據庫上檢索正確的審計。
但是,盡管我可以成功刪除父實體及其子實體並獲取父實體和子實體的審計數據,但對於這種情況,我無法弄清楚如何從數據庫中獲取分組的審計數據。
我嘗試使用 Audit.NET EntityFramework 的EntityFrameworkEvent.TransactionIdEntityFrameworkEvent.AmbientTransactionId ,但它們在數據庫上都是null

這是我的 POCO

public interface IAuditableEntity
{
    [NotMapped]
    string AuditAction { get; set; }

    [NotMapped]
    string AuditTransactionId { get; set; }

    [NotMapped]
    string AuditAmbientTransactionId { get; set; }
}

public class Scope : IAuditableEntity
{
    [Key]
    public int Id {get;set;}

    public string Name { get; set; }

    public virtual ICollection<Job> Jobs { get; set; }

    [NotMapped]
    string AuditAction { get; set; }

    [NotMapped]
    string AuditTransactionId { get; set; }

    [NotMapped]
    string AuditAmbientTransactionId { get; set; }
}

public class Job : IAuditableEntity
{
    [Key]
    public int Id {get;set;}

    public int ScopeId { get; set; }
    public virtual Scope Scope { get; set; }

    [StringLength(128)]
    public string Name { get; set; }

    [NotMapped]
    public string AuditAction { get; set; }

    [NotMapped]
    public string AuditTransactionId { get; set; }

    [NotMapped]
    public string AuditAmbientTransactionId { get; set; }
}

這是我的 Audit.NET 配置(來自 Startup.cs)

public class Startup
    {            
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddDbContext<AppAuditDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("AuditConnection")));

            #region Audit.NET

            var auditDbContextOptions = new DbContextOptionsBuilder<AppAuditDbContext>()
                .UseSqlServer(Configuration.GetConnectionString("AuditConnection"))
                .Options;

            Audit.Core.Configuration.Setup()
                .UseEntityFramework(x => x
                .UseDbContext<AppAuditDbContext>(auditDbContextOptions)
                .AuditTypeNameMapper(typeName =>
                {
                    return typeName;
                }).AuditEntityAction<IAuditableEntity>((ev, ent, auditEntity) =>
                {
                    var entityFrameworkEvent = ev.GetEntityFrameworkEvent();
                    if (entityFrameworkEvent == null) return;

                    auditEntity.AuditTransactionId = entityFrameworkEvent.TransactionId;
                    auditEntity.AuditAmbientTransactionId = entityFrameworkEvent.AmbientTransactionId;
                    auditEntity.AuditAction = ent.Action;
                }));

            #endregion


            services.AddControllersWithViews();
        }

        // other stuff..
    }

這是經過審計的上下文。

[AuditDbContext(IncludeEntityObjects = true)]
    public class ApplicationDbContext : AuditDbContext
    {

        public ApplicationDbContext([NotNullAttribute] DbContextOptions<ApplicationDbContext> options) : base(options)
        {
        }

        public DbSet<Scope> Scopes { get; set; }
        public DbSet<Job> Jobs { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Scope>().ToTable("Scope");

            modelBuilder.Entity<Job>().ToTable("Job");
        }

        public override int SaveChanges()
        {
            return base.SaveChanges();
        }

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

這是范圍的刪除 controller 操作。

public class ScopeController : Controller
    {
        private readonly ApplicationDbContext _context;

        public ScopeController(ApplicationDbContext context)
        {
            _context = context;
        }

        // Other controller actions...

        // POST: Scope/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            var scope = await _context.Scopes.Include(s => s.Jobs).SingleOrDefaultAsync(w => w.Id == id);            
            // using _context.Scopes.FindAsync(id) instead does delete the children Jobs without auditing it
            _context.Scopes.Remove(scope);
            await _context.SaveChangesAsync().ConfigureAwait(false);
            return RedirectToAction(nameof(Index));
        }
    }

這個 controller 操作從 EF 的角度來看是有效的。 它還審計父項和子項刪除操作,但我不知道如何將子項審計記錄與父項審計記錄相關聯我應該在代碼中的某處添加 AuditScope 嗎? 請問,我如何配置 Audit.NET 才能查詢審計數據庫以獲取分組的審計數據?

這是 ID #5 的 Scope 的審計跟蹤。
Scope 的審計跟蹤
Audit_Scope 表

這是 ScopeId #5 的作業的審計跟蹤。
作業的審計跟蹤
Audit_Job 表

鑒於提供的數據,假設我想讀取 Scope 的刪除審計(在本例中為 Audit_Scope 表中的 AuditId #9),包括對其子作業的刪除審計(在本例中為 Audit_Job 表中的 AuditId #10) . 我怎樣才能做到這一點?

謝謝,馬特奧

目前我剛剛向我的實體添加了一個自定義字段。 我在帶有 Guid 的自定義操作中重視它。

// EF AuditEventId per scope
Audit.Core.Configuration.AddCustomAction(ActionType.OnScopeCreated, scope =>
{
    var id = Guid.NewGuid();
    scope.SetCustomField("AuditScopeId", id);
});

這樣,與同一審計事件相關的ScopeJob表記錄將持有相同的AuditScopeId值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM