如何使用 Audit.Net - Audit.EntityFramework.Core 定位另一个数据库

How do I target another database with Audit.Net - Audit.EntityFramework.Core

我正在尝试从 Audit.Net repository 实施 Audit.EntityFramework.Core 包,但 运行 遇到了一些困难。我无法保存更改或定位到不同的数据库。我修改了我的 SaveChangesSaveChangesAsync 函数来调用 Audit.Net DbContextHelper class 的保存函数,但我遗漏了一些东西。

有没有办法做到以下几点?

  1. 使用从我正在尝试审计的 DbContext 继承的审计 DbContext 定位另一个数据库来存储审计数据?
    public class MyDbContext : DbContext {} //Types defined here
    public class AuditDbContext : MyDbContext {} //This context stores audit data into a different DB
    
  2. 在建立全局连接时不需要类型和它的审核类型之间的映射? (我试图避免使用当前正在发生大量变化的模型为每种类型显式调用 AuditTypeMapper )。
    //MyDbContext has different connection string than AuditDbContext
    Audit.Core.Configuration.Setup()
    .UseEntityFramework(x => x
        .UseDbContext<AuditDbContext>());
    

我试过类似于以下的代码,但在 SaveChanges 上出现运行时错误,表明没有设置模型。为 AuditDbContext 添加迁移没有帮助。

我明白我想做什么了。

我的设计目标是:

  1. 将审核记录存储在不同的数据库中
  2. 对每个类型进行审核 table 与审核类型相匹配(具有附加审核字段)
  3. 不需要维护单独的审计实体。操作数据库和审计数据库之间的更改应该是无缝的

我发现不起作用的是:

  1. 创建从我的操作 DbContext 继承的审计 DbContext 不起作用,因为关系、DBSet 和 ID 在审计数据库中无法以相同的方式处理。
  2. 使用 TypeBuilder 对操作类型的反射动态创建类型不起作用,因为 Audit.Net 在操作类型和审核类型之间转换对象以及从 CLR 类型转换为动态创建的类型失败。
  3. 混合具体类型和 EF Core 影子类型不起作用。

已采取的步骤

  1. 设置全局审计(在主要设置代码中)

    //Global setup of Auditing
    var auditDbCtxOptions = new DbContextOptionsBuilder<MyAuditDbContext>()
        .UseSqlServer(options.AuditDbConnectionString)
        .Options;
    
    Audit.Core.Configuration.Setup()
        .UseEntityFramework(x => x
            .UseDbContext<MyAuditDbContext>(auditDbCtxOptions)
            .AuditTypeNameMapper(typeName => 
            {
                return typeName;
            })
            .AuditEntityAction<AuditInfo>((ev, ent, auditEntity) =>
            {
                auditEntity.DatabaseAction = ent.Action;
            }));
    
  2. 如果我的审核模型继承自基类 AuditInfo

    public abstract class AuditInfo
    {
        public DateTime Created { get; set; }
        public DateTime? Updated { get; set; }
        public string CreatedBy { get; set; }
        public string UpdatedBy { get; set; }
    
        [NotMapped] //This is not mapped on the operational DB
        public string DatabaseAction { get; set; }
    }
    
  3. 使用新的 DbContextOnModelCreating

    创建了基于反射的审计模式
    public class MyAuditContext : DbContext
    {
        public MyAuditContext(DbContextOptions<MyAuditContext> options) : base(options)
        {
    
        }
    
        private readonly Type[] AllowedTypes = new Type[] 
        { 
            typeof(bool),
            typeof(int),
            typeof(decimal),
            typeof(string),
            typeof(DateTime),
        };
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            Console.WriteLine($"Generating dynamic audit model");
    
            //Go through each of the types in Hsa.Engine.Data.Models
            var asm = Assembly.GetExecutingAssembly();
            var modelTypes = asm.GetTypes()
                .Where(type => type.Namespace == "My.Data.Models.Namespace");
    
            //Create an entity For each type get all the properties on the model
            foreach(var model in modelTypes.Where(t => t.IsClass && !t.IsAbstract && t.BaseType == typeof(AuditInfo)))
            {
                Console.WriteLine($"Creating entity for {model.Name}");
    
                var table = modelBuilder.Entity(model, entity => 
                {
                    //Remove all types from base model, otherwise we get a bunch of noise about foreign keys, etc.
                    foreach(var prop in model.GetProperties())
                    {
                        entity.Ignore(prop.Name);
                    }
    
                    foreach(var prop in model.GetProperties().Where(p => AllowedTypes.Any(t => p.PropertyType.IsAssignableFrom(t))))
                    {
                        Console.WriteLine($"   Adding field: {prop.Name} - Type: {prop.PropertyType.Name}");
                        //Create a typed field for each property, not including ID or foreign key annotations (do include field lengths)
    
                        var dbField = entity.Property(prop.PropertyType, prop.Name);
    
                        if(prop.PropertyType.IsEnum)
                        {
                            dbField.HasConversion<string>();
                        }
    
                        if(dbField.Metadata.IsPrimaryKey())
                        {
                            dbField.ValueGeneratedNever(); //Removes existing model primary keys for the audit DB
                        }
                    }
    
                    //Add audit properties
                    entity.Property<int>("AuditId").IsRequired().UseSqlServerIdentityColumn();
                    entity.Property<DateTime>("AuditDate").HasDefaultValueSql("getdate()");
                    entity.Property<string>("DatabaseAction"); //included on AuditInfo but NotMapped to avoid putting it on the main DB. Added here to ensure it makes it into the audit DB
    
                    entity.HasKey("AuditId");
                    entity.HasIndex("Id");
                    entity.ToTable("Audit_" + model.Name);
                });
            }
    
            base.OnModelCreating(modelBuilder);
        }
    }
    
    
  4. 为主数据库和审计数据库创建迁移。

有些人可能不需要达到这些水平,但我想分享一下,以防有人在使用 Audit.Net

时需要类似的东西