简体   繁体   English

审计跟踪与实体框架

[英]audit trail with entity framework

I have fields for audit trail in each table (InsertedBy, InsertedDate, UpdatedBy and UpdatedDate), I build solution to reduce redundant before by override savechange(): 我有每个表中的审计跟踪字段(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;
            }
        }
    }
}

the question is: is using reflection within each entity before modified or added waste in memory and performance ? 问题是:在修改之前是在每个实体中使用反射还是在内存和性能上添加了浪费? if yes is there is best practice for this ? 如果是,那么最佳做法是什么? is this another code snippet better in performance or just use reflection also? 这是另一个更好的性能代码片段还是只使用反射?

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;
    }
}

is entityEntry.Property("InsertedBy") uses reflection ? entityEntry.Property(“InsertedBy”)使用反射?

Reflection is slow (slow is subjective) and if you want to avoid it, then you need to get rid of such code as below: 反射很慢(慢是主观的)如果你想避免它,那么你需要摆脱下面的代码:

Type type = entityEntry.Entity.GetType();
if (type.GetProperty("InsertedBy") != null)

Even if it was not slow, the code above is still "buggy" because a programmer may mistakenly write InsertBy instead of InsertedBy . 即使它不慢,上面的代码仍然是“错误”,因为程序员可能会错误地编写InsertBy而不是InsertedBy This can easily be avoided with help from the compiler using the approach below. 在编译器的帮助下使用下面的方法可以很容易地避免这种情况。

Use an interface and implement it in all entities that require audit. 使用接口并在需要审计的所有实体中实现它。

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
        }
    }
}

As mentioned in the comments, you will get compiler support and reflection is not used anymore. 正如评论中所提到的,您将获得编译器支持,并且不再使用反射。 I would even go a step further and not pass the int userId . 我甚至更进一步,不传递int userId I will bring the code for figuring out the userId and put it in this class. 我将带来用于计算userId的代码并将其放入此类中。 That way the class is self sufficient and clients do not need to provide it this information. 这样,这个课程就是自给自足的,客户不需要提供这些信息。

Usage: 用法:

var e = new SomeEntity();
var auditor = new Auditor<SomeEntity>();
auditor.ApplyAudit(e, 1); // 1 is userId, I am just hardcoding for brevity

Or use it from your context: 或者从您的上下文中使用它:

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();
}

This means that all entities who are auditable will need to implement the IAuditable interface. 这意味着所有可审计的实体都需要实现IAuditable接口。 EF generates partial classes for your entities but do not modify those partial classes because the next time you run the custom tool, it will be wiped out. EF为您的实体生成部分类,但不修改这些部分类,因为下次运行自定义工具时,它将被清除。

Instead, create another partial class with the same name and implement the IAuditable . 而是创建另一个具有相同名称的IAuditable类并实现IAuditable

public partial class SomeEntity : IAuditable {}

An even better approach is to create a custom T4 template so it creates the partial class with the code : IAuditable . 更好的方法是创建自定义T4模板,以便使用代码创建部分类: IAuditable Please see this article for how to do that. 请参阅此文章以了解如何执行此操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM