实体框架 6:审计/跟踪更改

Posted

技术标签:

【中文标题】实体框架 6:审计/跟踪更改【英文标题】:Entity Framework 6: audit/track changes 【发布时间】:2014-12-08 22:23:06 【问题描述】:

我的核心项目是用 C# 编写的。

我在一个数据库上工作,其中一些表具有“user_mod”和“date_mod”列,用于标记谁以及何时制作了一些 mod,并且与“data_new”和“user_new”相同。

我的问题:有没有办法集中这个并自动插入这些数据,我在其中创建dbContext 的实例?

如果没有,我将使用审计跟踪工具。我见过其中的一些,但有一个问题:所有这些都需要我的模型中的一些代码。但我不想写在我的模型中,因为如果我必须更改它,我会丢失 mods。是否可以在不写入模型文件的情况下对 EF6 使用审计跟踪?怎么样?

编辑:

我尝试覆盖 saveChanges。

public partial class PieEntities : DbContext

    public override int SaveChanges(System.Data.Objects.SaveOptions options)
    
        var timestamp = DateTime.Now;

        EntityState es = EntityState.Added;
        ObjectStateManager o = new ObjectStateManager();

        foreach (ObjectStateEntry entry in o.GetObjectStateEntries(EntityState.Added ))  
            if (entry.Entity.GetType() == typeof(TabImpianti)) 
                TabImpianti impianto = entry.Entity as TabImpianti;
                impianto.DATA_INS = timestamp;
                impianto.DATA_MOD = timestamp;
                string u = mdlImpostazioni.p.UserName;
                impianto.USER_INS = u;
                impianto.USER_MOD = u;
            
        
        return base.SaveChanges(options);
    

更新:我总结了solution here。

【问题讨论】:

Entity Framework DbContext SaveChanges() OriginalValue Incorrect 的可能副本。在这方面还有很多努力。寻找实体框架 + 审计。 @GertArnold 为什么?我不这么认为。此外,这个问题是关于 EF4,我的两个旧版本。 好吧,当我在 *** 上搜索这个主题时,我从一长串点击中随机挑选了一个。它通常相当于覆盖 SaveChanges,这在 EF4 中是相同的。在 EF6 中,您可能会在命令树拦截器领域冒险,但我不确定这会将您带到哪里。 @PieroAlberto 你的 ObjectStateManager 必须来自上下文:从这个(this as IObjectContextAdapter).ObjectStateManager分配它 关于碱基调用,我不明白它为什么会造成问题。我认为您不需要再次告诉编译器您是从 DbContext 继承的,删除 : DbContext 【参考方案1】:

如果使用 EF6 的 DbContext,您可以在 SaveChanges 覆盖中使用 ChangeTracker 来查找添加/修改的自定义类型实体,例如 IAuditedEntity。

public interface IAuditedEntity 
  string CreatedBy  get; set; 
  DateTime CreatedAt  get; set; 
  string LastModifiedBy  get; set; 
  DateTime LastModifiedAt  get; set; 


public override int SaveChanges() 
  var addedAuditedEntities = ChangeTracker.Entries<IAuditedEntity>()
    .Where(p => p.State == EntityState.Added)
    .Select(p => p.Entity);

  var modifiedAuditedEntities = ChangeTracker.Entries<IAuditedEntity>()
    .Where(p => p.State == EntityState.Modified)
    .Select(p => p.Entity);

  var now = DateTime.UtcNow;

  foreach (var added in addedAuditedEntities) 
    added.CreatedAt = now;
    added.LastModifiedAt = now;
  

  foreach (var modified in modifiedAuditedEntities) 
    modified.LastModifiedAt = now;
  

  return base.SaveChanges();

【讨论】:

您的代码看起来很简单,但我刚刚尝试过...我在我的一个表中插入了一个新项目,但它没有输入到 addedEntities 的 for 中。它是如何工作的?我该怎么办? 您要跟踪/审计的实体必须实现IAuditedEntity 我的实体?但我的实体是在模型中定义的......我不想手动修改自动生成的模型。我该怎么做? (请原谅我的问题) EF 数据库第一个实体用 partial 关键字标记。这使得在不干扰生成代码的情况下分配属性变得容易。只需创建一个新文件AuditedEntries.cs 包含[IAuditedEntity] public partial class TabImpianti 要清楚一点,因为每个实体都实现了IAuditedEntity 接口,所以属性是各个实体的一部分,所以它们被保存到同一个表中对吗?【参考方案2】:

有一种方法可以做到这一点:您可以创建一个与您的对象上下文同名的分部类,并实现对SaveChanges 方法的覆盖。在此覆盖中,您可以查看将推送到数据库的所有更改并进行处理。

您可以按照自己喜欢的方式处理它们,在以下示例中,我创建了一个接口IAutoTimestampEntity,其中包含创建日期和修改日期。这种类型的任何对象都会随着时间的变化而自动更新。

public override int SaveChanges(System.Data.Objects.SaveOptions options)

    var timestamp = DateTime.Now;

    foreach (var InsertedAutoTimestampEntity in ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added).Select(ose => ose.Entity).OfType<IAutoTimestampEntity>())
    
        InsertedAutoTimestampEntity.CreationDate = timestamp;
        InsertedAutoTimestampEntity.ModificationDate = timestamp;
    

    foreach (var UpdatedAutoTimestampEntity in ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Modified).Select(ose => ose.Entity).OfType<IAutoTimestampEntity>())
    
        UpdatedAutoTimestampEntity.ModificationDate = timestamp;
    

    return base.SaveChanges(options);

您可以使用相同的原理,也可以详细查看每个更改实体的类型。不过,我喜欢界面的声明性方面。它允许您显式地公开自动化的一个方面,而不是让它由 EF 层静默完成。

如果您有DbContext 而不是ObjectContext,请将您的DbContext 转换为IObjectContextAdapter 以访问ObjectStateManager

【讨论】:

不错!!是否也可以看到什么是删除?什么是 InsertedAutoTimestampEntity 和 UpdatedAutoTimestampEntity? InsertedAutoTimeStampEntity 和 UpdatedAutoTimestampEntity 是我在枚举中给实体起的名字,所以只是命名。 关于删除,这里是 EntityState 枚举,您可以看到已删除以及您可以跟踪的其他状态:msdn.microsoft.com/en-us/library/… 我添加了对 System.entity 的引用。但它说“ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added)”它不是静态方法,我必须实例化它。为什么你的代码不是这样的? 如果您使用的是DbContext 而不是ObjectContext,请将您的DbContext 转换为IObjectContextAdapter 以访问ObjectStateManager【参考方案3】:

补充来自@AlaaMasoud 的答案。

使用CreatedDateUpdatedDate

interface IEntityDate

    DateTime CreatedDate  get; set; 

    DateTime UpdatedDate  get; set; 


public abstract class EntityBase<T1>: IEntityDate

    public T1 Id  get; set; 

    public virtual DateTime CreatedDate  get; set; 
    public virtual string CreatedBy  get; set; 
    public virtual DateTime UpdatedDate  get; set; 
    public virtual string UpdatedBy  get; set; 


public override int SaveChanges()

    var now = DateTime.Now;

    foreach (var changedEntity in ChangeTracker.Entries())
    
        if (changedEntity.Entity is IEntityDate entity)
        
            switch (changedEntity.State)
            
                case EntityState.Added:
                    entity.CreatedDate = now;
                    entity.UpdatedDate = now;
                    break;

                case EntityState.Modified:
                    Entry(entity).Property(x => x.CreatedDate).IsModified = false;
                    entity.UpdatedDate = now;
                    break;
            
        
    

    return base.SaveChanges();

要处理CreatedByUpdatedBy,我使用DbContext 的包装器,如下所示:

public interface IEntity

    DateTime CreatedDate  get; set; 

    string CreatedBy  get; set; 

    DateTime UpdatedDate  get; set; 

    string UpdatedBy  get; set; 


public interface ICurrentUser

    string GetUsername();


public class ApplicationDbContextUserWrapper

    public ApplicationDbContext Context;

    public ApplicationDbContextUserWrapper(ApplicationDbContext context, ICurrentUser currentUser)
    
        context.CurrentUser = currentUser;
        this.Context = context;
    


public class ApplicationDbContext : DbContext


    public ICurrentUser CurrentUser;
    
    public override int SaveChanges()
    
        var now = DateTime.Now;

        foreach (var changedEntity in ChangeTracker.Entries())
        
            if (changedEntity.Entity is IEntity entity)
            
                switch (changedEntity.State)
                
                    case EntityState.Added:
                        entity.CreatedDate = now;
                        entity.UpdatedDate = now;
                        entity.CreatedBy = CurrentUser.GetUsername();
                        entity.UpdatedBy = CurrentUser.GetUsername();
                        break;
                    case EntityState.Modified:
                        Entry(entity).Property(x => x.CreatedBy).IsModified = false;
                        Entry(entity).Property(x => x.CreatedDate).IsModified = false;
                        entity.UpdatedDate = now;
                        entity.UpdatedBy = CurrentUser.GetUsername();
                        break;
                
            
        

        return base.SaveChanges();
    
    
    ...

来源:

https://***.com/a/53669556/3850405

【讨论】:

以上是关于实体框架 6:审计/跟踪更改的主要内容,如果未能解决你的问题,请参考以下文章

如何使用trackerenableddbcontext在asp.net mvc5和代码中的实体框架中实现审计跟踪

为每次保存业务实体生成审计跟踪的可能方法

实体框架条目更改跟踪问题

为啥实体框架 System.Data.Entity.Core.Objects.RelationshipEntry 错误? (用于更改跟踪)

实体框架 6:在上下文中禁用跟踪时更新实体

使用 Entity Framework Core 进行审计跟踪