EF Core如何实施对价值对象变更的审核日志

时间:2018-07-16 19:40:35

标签: c# entity-framework asp.net-core domain-driven-design entity-framework-core

我正在使用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上的非值对象属性列出了它们的旧值/新值,其中对值对象的更改仅列出了新值:

enter image description here

是否可以获取值对象的先前值?

更新:

因此,对于我的值对象所做的更改,OldValues列为null的原因是由于对 Added 进行更改时,值对象的状态。我在switch语句中添加了对IsOwned()方法的调用,并尝试像下面这样来获取property.OriginalValue:

                        case EntityState.Added:

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

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

enter image description here

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

1 个答案:

答案 0 :(得分:0)

代替

auditEntry.OldValues[propertyName] = property.OriginalValue;

使用

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