我试图实现Audit.EntityFramework.Core
从包装Audit.Net仓库 ,但我遇到了一些困难。 我无法保存更改或针对不同的数据库。 我已经修改了我SaveChanges
和SaveChangesAsync
函数调用Audit.Net
DbContextHelper
类的保存功能,但我失去了一些东西。
有没有办法做到以下几点?
- 目标使用审计存储审核数据的数据库
DbContext
从继承DbContext
我试图审计? public class MyDbContext : DbContext {} //Types defined here public class AuditDbContext : MyDbContext {} //This context stores audit data into a different DB
- 建立一个全球性的连接时不需要类型及其审计类型之间的映射? (我试图避免调用
AuditTypeMapper
明确了每种类型,因此目前经历了很多变化的模型)。 //MyDbContext has different connection string than AuditDbContext Audit.Core.Configuration.Setup() .UseEntityFramework(x => x .UseDbContext<AuditDbContext>());
我已经试过了类似于以下,但得到的运行时错误代码SaveChanges
指示没有模型建立。 添加了一个迁移AuditDbContext
没有帮助。
我想通了什么,我要怎样做。
我的设计目标是:
- 在不同的数据库中存储的审计记录
- 有每种类型相匹配的审计类型(有额外的审计字段)的审核表
- 不需要单独的审计实体的保养。 操作DB和DB审计之间的变化应该是无缝
的事情,我发现,没有进行工作:
- 创建从我工作的DbContext继承不起作用,因为的关系,DBSets和ID的不能在审计DB中同样的方式对待审计的DbContext。
- 动态使用反射在整个工作类型与TypeBuilder不起作用,因为Audit.Net投射它们的操作和审计类型和铸造之间的对象从一个CLR类型的动态创建的类型创建失败类型。
- 搅拌混凝土类型和EF核心阴影类型不起作用。
所采取的步骤
设立全球审计(在主设置代码)
//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; }));
有我的经审计的型号,从基类继承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; } }
创建使用新的基于反射的审计模式DbContext
和OnModelCreating
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); } }
- 创建对于主数据库和审计数据库迁移。
有些人可能不需要去这些水平,但我想分享的情况下,任何人使用时需要类似的东西Audit.Net
文章来源: How do I target another database with Audit.Net - Audit.EntityFramework.Core