EF Core 如何实现对值对象更改的审计日志

Posted

技术标签:

【中文标题】EF Core 如何实现对值对象更改的审计日志【英文标题】:EF Core how to implement audit log of changes to value objects 【发布时间】:2018-12-24 10:15:27 【问题描述】:

我正在使用 EF Core/.NET Core 2.1,并遵循 DDD。我需要实现对我的实体的所有更改的审核日志,并且使用来自this blog post 的代码(下面包含此帖子的相关代码)完成了此操作。此代码有效并跟踪对任何属性的更改,但是当它记录对我的值对象的更改时,它只列出新值,而不列出旧值。

一些代码:

public class Item

    protected Item()

    //truncated for brevity

    public Weight Weight  get; private set; 


public class Weight : ValueObject<Weight>

    public WeightUnit WeightUnit  get; private set; 
    public double WeightValue  get; private set; 

    protected Weight()  

    public Weight(WeightUnit weightUnit, double weight)
    
        this.WeightUnit = weightUnit;
        this.WeightValue = weight;
    

以及我的上下文类中的审计跟踪代码

public class MyContext : DbContext

    //truncated for brevity

    public override int SaveChanges(bool acceptAllChangesOnSuccess)
    
        var auditEntries = OnBeforeSaveChanges();
        var result = base.SaveChanges(acceptAllChangesOnSuccess);
        OnAfterSaveChanges(auditEntries);
        return result;
    

    private List<AuditEntry> OnBeforeSaveChanges()
    
        if (!this.AuditingAndEntityTimestampingEnabled)
        
            return null;
        

        ChangeTracker.DetectChanges();
        var auditEntries = new List<AuditEntry>();
        foreach (var entry in ChangeTracker.Entries())
        
            if (entry.Entity is Audit || entry.State == EntityState.Detached || entry.State == EntityState.Unchanged)
            
                continue;
            

            var auditEntry = new AuditEntry(entry)
            
                TableName = entry.Metadata.Relational().TableName
            ;
            auditEntries.Add(auditEntry);

            foreach (var property in entry.Properties)
            
                if (property.IsTemporary)
                
                    // value will be generated by the database, get the value after saving
                    auditEntry.TemporaryProperties.Add(property);
                    continue;
                

                string propertyName = property.Metadata.Name;
                if (property.Metadata.IsPrimaryKey())
                
                    auditEntry.KeyValues[propertyName] = property.CurrentValue;
                    continue;
                

                switch (entry.State)
                
                    case EntityState.Added:
                        auditEntry.NewValues[propertyName] = property.CurrentValue;
                        break;

                    case EntityState.Deleted:
                        auditEntry.OldValues[propertyName] = property.OriginalValue;
                        break;

                    case EntityState.Modified:
                        if (property.IsModified)
                        
                            auditEntry.OldValues[propertyName] = property.OriginalValue;
                            auditEntry.NewValues[propertyName] = property.CurrentValue;
                        
                        break;
                
            
        

        // Save audit entities that have all the modifications
        foreach (var auditEntry in auditEntries.Where(_ => !_.HasTemporaryProperties))
        
            Audits.Add(auditEntry.ToAudit());
        

        // keep a list of entries where the value of some properties are unknown at this step
        return auditEntries.Where(_ => _.HasTemporaryProperties).ToList();
    


这是审计更改如何持续到数据库的屏幕截图。 Item 上的非值对象属性列出了它们的旧/新值,其中对值对象的更改仅列出新值:

有没有办法获取值对象以前的值?

更新:

因此,我的值对象更改时,OldValues 列为空的原因是值对象的状态在更改时已添加。我在 switch 语句中添加了对 IsOwned() 方法的调用,并尝试像这样在其中获取 property.OriginalValue:

                        case EntityState.Added:

                        if (entry.Metadata.IsOwned())
                        
                            auditEntry.OldValues[propertyName] = property.OriginalValue;
                        
                        auditEntry.NewValues[propertyName] = property.CurrentValue;
                        break;

但是,这只是记录值对象正在更新到的当前值。

所以问题仍然存在 - 是否有任何方法可以使用 EF Core ChangeTracker 获取值对象的先前值,或者由于我的审计要求,我是否需要重新考虑我对 DDD 值对象的使用?

【问题讨论】:

一目了然,您需要在 entry.Properties 上使用递归方法。 该递归方法会做什么来获取值对象的先前值? 了解 EF 如何跟踪拥有的实体类型。您可能需要在EF Core 上提问,并为他们提供完整的列表或项目。 @G_P 知道你当时是如何解决这个问题的吗?我收到了这个问题***.com/questions/58299469/… @Bharat 我还没有解决值对象问题,但是我在你的问题中添加了一个可能的答案。 【参考方案1】:

不是

auditEntry.OldValues[propertyName] = property.OriginalValue;

使用

auditEntry.OldValues[propertyName] = entry.GetDatabaseValues().GetValue<object>(propertyName).ToString();

【讨论】:

以上是关于EF Core 如何实现对值对象更改的审计日志的主要内容,如果未能解决你的问题,请参考以下文章

Core + Vue 后台管理基础框架6——业务日志

如何将同一列添加到 EF Core 中的所有实体?

如何在 asp.net mvc 中创建审计日志/审计跟踪

基于ef core 2.0的数据库增删改审计系统

字段更改的休眠审计日志

如何跳过ef core中的某些迁移?