首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Audit.NET实体框架核心相关实体管理

Audit.NET实体框架核心相关实体管理
EN

Stack Overflow用户
提问于 2020-04-23 11:02:32
回答 1查看 2K关注 0票数 5

我正在开发一个ASP.NET Core3.1Web应用程序。我想在数据库中持久化数据时添加审计跟踪/日志。

我从this SO answer那里得到灵感,开始在一个测试项目中使用Audit.NET。

以下是我的目标(类似于相关的SO线程):

将审计记录存储在不同的数据库中:几乎使用额外的AppAuditDbContext;

  • Have完成--每个类型的审计表与已审计类型(有其他审计字段)相匹配:通过在额外的AppAuditDbContext;

  • Require上反射,而不是单独审计实体的维护,完成了。操作数据库和审计数据库之间的更改应该是无缝的:通过对相关实体的已审计entities;

  • Retrieve审计数据的额外AppAuditDbContextDataAnnotations进行反思,完成了到DO

此时,我可以创建一个独立的已审计实体,并检索对审计数据库的正确审计。

但是,虽然我可以成功地删除带有其子实体的父实体,并获得父实体和子实体的审计数据,但对于这样的场景,我不知道如何从DB获得分组审计数据。

我试着使用Audit.NET EntityFramework的EntityFrameworkEvent.TransactionIdEntityFrameworkEvent.AmbientTransactionId,但它们都是DB上的null

这是我的便便

代码语言:javascript
复制
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)

代码语言:javascript
复制
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..
    }

这是经过审核的上下文。

代码语言:javascript
复制
[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);
        }
    }

这是范围的Delete控制器操作。

代码语言:javascript
复制
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));
        }
    }

此控制器操作从EF的角度工作。它还会审计父级和子级的删除操作,但是我不知道如何将子审计记录与父审计记录关联起来,我应该在代码中的某个地方添加一个AuditScope吗?请注意,如何将Audit.NET配置为能够查询审核数据库以获得分组审核数据?

下面是Id为5的作用域的审核跟踪。

Audit_Scope表

下面是带有ScopeId #5的作业的审核跟踪。

Audit_Job表

考虑到所提供的数据,假设我希望阅读范围的删除审计(在本例中,是从Audit_Scope表中的Audit_Scope #9 ),包括对其子作业的删除审计(在本例中,是从Audit_Job表中的AuditId #10 )。我怎样才能做到这一点?

谢谢你,马泰奥

EN

回答 1

Stack Overflow用户

发布于 2020-04-30 07:01:27

目前,我刚刚在实体中添加了一个自定义字段。我在使用Guid的自定义操作中评估它。

代码语言:javascript
复制
// EF AuditEventId per scope
Audit.Core.Configuration.AddCustomAction(ActionType.OnScopeCreated, scope =>
{
    var id = Guid.NewGuid();
    scope.SetCustomField("AuditScopeId", id);
});

这样,与同一个审计事件相关的Scope表记录和Job表记录都将包含相同的AuditScopeId值。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61385646

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档