问题描述
我正在尝试通过Audit.EntityFramework.Core 包 tree / master / src / Audit.EntityFramework rel = nofollow noreferrer> Audit.Net存储库,但遇到了一些困难。我无法保存更改或定位到其他数据库。我已经修改了 SaveChanges
和 SaveChangesAsync
函数以调用 Audit.Net
DbContextHelper
类的保存功能,但是我缺少了一些东西。
I'm trying to implement the Audit.EntityFramework.Core
package from the Audit.Net repository but am running into some difficulty. I'm unable to save changes or target a different database. I've modified my SaveChanges
and SaveChangesAsync
function to call the Audit.Net
DbContextHelper
class's save functions but I'm missing something.
有没有一种方法可以
- 使用继承的审计
DbContext
定位另一个数据库来存储审计数据从DbContext
我要审核吗?
- Target another database for storing audit data using an audit
DbContext
that inherits from theDbContext
I'm trying to audit?
public class MyDbContext : DbContext {} //Types defined here
public class AuditDbContext : MyDbContext {} //This context stores audit data into a different DB
AuditTypeMapper
)。 AuditTypeMapper
explicitly for each type with a model that's currently undergoing a lot of change).//MyDbContext has different connection string than AuditDbContext
Audit.Core.Configuration.Setup()
.UseEntityFramework(x => x
.UseDbContext<AuditDbContext>());
我尝试了类似于以下代码但是在 SaveChanges
上出现运行时错误,表明未设置模型。为 AuditDbContext
添加迁移没有帮助。
I've tried code that resembles the following but get runtime errors on SaveChanges
that indicate that there is no model set up. Adding a migration for the AuditDbContext
didn't help.
推荐答案
我弄清楚了我要做什么。
我的设计目标是:
I figured out what I was trying to do.
My design goals were:
- 将审核记录存储在另一个数据库中
- 每个审核表都有一个审核表与已审核类型匹配的类型(带有其他审核字段)
- 不需要维护单独的审核实体。业务数据库和审计数据库之间的更改应该是无缝的
我发现无效的是:
Things I discovered that did not work were:
- 创建从我的可操作DbContext继承的审计DbContext无法正常工作,因为在审计数据库中不能以相同的方式处理关系,DBSet和ID。
- 使用TypeBuilder对操作类型进行反射来动态创建类型是行不通的,因为Audit.Net在操作类型和审计类型之间强制转换对象,并且无法将CLR类型转换为动态创建的类型。
- 混合具体类型和EF Core阴影类型无效。
已采取的步骤
Steps Taken
-
设置全局审核(在主要设置代码中)
Set up Global Auditing (in main setup code)
//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; }
}
使用新的<$创建基于反射的审核架构c $ c> 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
Some people may not need to go to these levels but I wanted to share in case anyone needed something similar when using Audit.Net
这篇关于如何使用Audit.Net定位另一个数据库-Audit.EntityFramework.Core的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!