我在每个表中都有审计跟踪的字段(InsertedBy,InsertedDate,UpdatedBy和UpdatedDate),我通过覆盖savechange()构建了减少冗余的解决方案:
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries().Where(e =>
e.State == System.Data.Entity.EntityState.Added || e.State == System.Data.Entity.EntityState.Modified))
{
Auditing.ApplyAudit(entry, User);
}
return base.SaveChanges();
}
public class Auditing
{
public static void ApplyAudit(DbEntityEntry entityEntry, int User)
{
Type type = entityEntry.Entity.GetType();
if (entityEntry.State.ToString() == "Added")
{
if (type.GetProperty("InsertedBy") != null)
{
entityEntry.Property("InsertedBy").CurrentValue = User;
}
if (type.GetProperty("InsertedDate") != null)
{
entityEntry.Property("InsertedDate").CurrentValue = DateTime.Now;
}
}
else if (entityEntry.State.ToString() == "Modified")
{
if (type.GetProperty("InsertedBy") != null)
{
entityEntry.Property("InsertedBy").IsModified = false;
}
if (type.GetProperty("InsertedDate") != null)
{
entityEntry.Property("InsertedDate").IsModified = false;
}
if (type.GetProperty("UpdatedBy") != null)
{
entityEntry.Property("UpdatedBy").CurrentValue = User;
}
if (type.GetProperty("UpdatedDate") != null)
{
entityEntry.Property("UpdatedDate").CurrentValue = DateTime.Now;
}
}
}
}
问题是: 在修改之前在每个实体中使用反射或在内存和性能中添加浪费?如果是,那么最佳做法是什么? 这是另一个性能更好的代码片段还是只使用反射?
public static void ApplyAudit(DbEntityEntry entityEntry, long User)
{
if (entityEntry.State.ToString() == "Added")
{
entityEntry.Property("InsertedBy").CurrentValue = User;
entityEntry.Property("InsertedDate").CurrentValue = DateTime.Now;
}
else if (entityEntry.State.ToString() == "Modified")
{
entityEntry.Property("InsertedBy").IsModified = false;
entityEntry.Property("InsertedDate").IsModified = false;
entityEntry.Property("UpdatedBy").CurrentValue = User;
entityEntry.Property("UpdatedDate").CurrentValue = DateTime.Now;
}
}
是entityEntry.Property(“InsertedBy”)使用反射?
答案 0 :(得分:3)
反射很慢(慢是主观的)如果你想避免它,那么你需要摆脱下面的代码:
Type type = entityEntry.Entity.GetType();
if (type.GetProperty("InsertedBy") != null)
即使它不慢,上面的代码仍然是" buggy"因为程序员可能会错误地写InsertBy
而不是InsertedBy
。在编译器的帮助下使用下面的方法可以很容易地避免这种情况。
使用界面并在需要审核的所有实体中实施。
public interface IAuditable
{
string InsertedBy { get; set; }
// ... other properties
}
public class SomeEntity : IAuditable
{
public string InsertedBy { get; set; }
}
public class Auditor<TAuditable> where TAuditable : IAuditable
{
public void ApplyAudit(TAuditable entity, int userId)
{
// No reflection and you get compiler support
if (entity.InsertedBy == null)
{
// whatever
}
else
{
// whatever
}
}
}
如评论中所述,您将获得编译器支持,并且不再使用反射。我甚至更进一步,不通过int userId
。我将带来用于计算userId
的代码并将其放入此类中。这样,这个课程就足够了,客户不需要提供这些信息。
用法:
var e = new SomeEntity();
var auditor = new Auditor<SomeEntity>();
auditor.ApplyAudit(e, 1); // 1 is userId, I am just hardcoding for brevity
或者从您的上下文中使用它:
public override int SaveChanges()
{
var auditables = ChangeTracker.Entries().Where(e =>
e.State == System.Data.Entity.EntityState.Added || e.State == System.Data.Entity.EntityState.Modified)
.OfType<IAuditable>();
var auditor = new Auditor<IAuditable>();
foreach (var entry in auditables)
{
// 1 is userId, I am just hardcoding for brevity
auditor.ApplyAudit(entry, 1);
}
return base.SaveChanges();
}
这意味着所有可审计的实体都需要实现IAuditable
接口。 EF为您的实体生成部分类,但不修改这些部分类,因为下次运行自定义工具时,它将被清除。
而是创建另一个具有相同名称的分部类并实现IAuditable
。
public partial class SomeEntity : IAuditable {}
更好的方法是创建自定义T4模板,以便创建代码为: IAuditable
的分部类。有关如何操作的信息,请参阅this article。