EF Core如何实现值对象的变更审计日志记录

10
我正在使用EF Core / .NET Core 2.1,并遵循DDD。我需要实现对所有实体更改的审计日志,使用此博客文章中的代码(包括该文章中的相关代码)。这段代码可以跟踪任何属性的更改,但是当它记录值对象的更改时,它只列出新值,而没有旧值。

部分代码:

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上的非值对象属性列出了它们的旧/新值,而对于值对象的更改仅列出了新值:

enter image description here

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

更新:

因此,我的值对象更改时 OldValues 列为空的原因是值对象的 State 为 Added。我在 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值对象的方法?


1
一眼看去,您需要在 entry.Properties 上使用递归方法。 - Mark G
那个递归方法会怎样获取值对象的先前值? - G_P
我不知道EF如何跟踪拥有的实体类型。你可能需要在EF Core上提问,并为他们提供完整的清单或项目。 - Mark G
@G_P,你当时是如何解决这个问题的?我现在也遇到了这个问题:https://dev59.com/vrbna4cB1Zd3GeqPg8uI - Bharat
@Bharat 我还没有解决值对象的问题,但是我在你的问题中添加了一个可能的答案。 - G_P
可能的答案来解决值对象问题 https://dev59.com/Crroa4cB1Zd3GeqPdBWc - Simopaa
1个回答

1
替代 `

` 标签。

auditEntry.OldValues[propertyName] = property.OriginalValue;

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接