所以假设我有一个名为Record的表。我有一行(ID:1,Name:One,Version:1)。当我把它改为(ID1:Name:Changed,Version:2)时,Record_History表会增加一行(ID:1,Name:One,Version:1)。
我已经看过很好的例子并知道有使用Entity Framework来保持更改审计日志的库,但我需要每个修订版的实体的完整快照以供SQL报告使用。
在我的C#代码中,我有一个基类,所有我的“Tracked”表对应的实体类都继承自该基类:
public abstract class TrackedEntity
{
[Column(TypeName = "varchar")]
[MaxLength(48)]
[Required]
public string ModifiedBy { get; set; }
[Required]
public DateTime Modified { get; set; }
public int Version { get; set; }
}
我的一个实体类的示例是:
public sealed class Record : TrackedEntity
{
[Key]
public int RecordID { get; set; }
[MaxLength(64)]
public string Name { get; set; }
}
现在让我们来看看我遇到的难题。我不想为每个实体单独创建和维护一个_History类。我想要做一些智能的事情,告诉我的DbContext类它所拥有的每个DbSet都应该有一个与TrackedEntity继承类型相关的历史对应表,并且无论何时保存该类型的实体,都将原始值从数据库复制到历史表中。
因此,在我的DbContext类中,我有一个记录的DbSet(以及其他实体的更多DbSets)。
public DbSet<Record> Records { get; set; }
我已经重写了OnModelCreating方法,以便注入新的_History表的映射。但是我无法想出如何使用反射将每个实体的类型传递给DbModelBuilder。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//map a history table for each tracked Entity type
PropertyInfo[] properties = GetType().GetProperties();
foreach (PropertyInfo property in properties.Where(p => p.PropertyType.IsGenericType
&& p.PropertyType.Name.StartsWith("DbSet")
&& p.PropertyType.GetGenericArguments().Length > 0
&& p.PropertyType.GetGenericArguments()[0].IsSubclassOf(typeof(TrackedEntity))))
{
Type type = property.PropertyType.GetGenericArguments()[0];
modelBuilder.Entity<type>().Map(m => //code breaks here, I cannot use the type variable it expects a hard coded Type
{
m.ToTable(type.Name + "_History");
m.MapInheritedProperties();
});
}
}
我甚至不确定像这样使用modelBuilder是否会生成新的History表。我也不知道如何处理保存,我不确定实体映射是否意味着更改会同时保存在两个表中?我可以在我的DbContext中创建一个SaveChanges方法,可以循环遍历我的实体,但我不知道如何使实体保存到第二个表中。
public int SaveChanges(string username)
{
//duplicate tracked entity values from database to history tables
PropertyInfo[] properties = GetType().GetProperties();
foreach (PropertyInfo property in properties.Where(p => p.PropertyType.IsGenericType
&& p.PropertyType.Name.StartsWith("DbSet")
&& p.PropertyType.GetGenericArguments().Length > 0
&& p.PropertyType.GetGenericArguments()[0].IsSubclassOf(typeof(TrackedEntity))))
{
foreach (TrackedEntity entity in (DbSet<TrackedEntity>)property.GetValue(this, null))
{
entity.Modified = DateTime.UtcNow;
entity.ModifiedBy = username;
entity.Version += 1;
//Todo: duplicate entity values from database to history tables
}
}
return base.SaveChanges();
}
非常抱歉问题很长,这是一个相当复杂的问题。任何帮助将不胜感激。