【问题标题】:Show Original Values Entity Framework 7显示原始值实体框架 7
【发布时间】:2015-09-15 23:52:33
【问题描述】:

我有一个跟踪添加、删除和修改的审计表。由于多种原因,我在实体框架中跟踪这一点,而不是使用数据库触发器,但实际上是因为我们使用了进程帐户,并且我想跟踪哪些用户对该记录进行了物理更改。

我曾在 EF 5 中使用过此功能,但我不记得我可能也在 EF6 中使用过它。无论哪种方式,我都在尝试捕获原始值的 EF 7 中度过最艰难的时期。

我注意到当我在手表中时 - 我可以看到非公开成员内部的原始值 - 所以在我的脑海中我知道它必须存在于某个地方。

最终这在 EF 早期版本中有效:

EntityEntry dbEntry; //this is actually passed in a different area just showing as an example.

foreach (string propertyName in dbEntry.OriginalValues.PropertyNames)
{
    // For updates, we only want to capture the columns that actually changed
    if (!object.Equals(dbEntry.OriginalValues.GetValue<object>(propertyName), dbEntry.CurrentValues.GetValue<object>(propertyName)))
    {
        result.Add(new TableChange()
        {
            AuditLogID = Guid.NewGuid(),
            UserID = userId,
            EventDateUTC = changeTime,
            EventType = "M",    // Modified
            TableName = tableName,
            RecordID = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(),
            ColumnName = propertyName,
            OriginalValue = dbEntry.OriginalValues.GetValue<object>(propertyName) == null ? null : dbEntry.OriginalValues.GetValue<object>(propertyName).ToString(),
            NewValue = dbEntry.CurrentValues.GetValue<object>(propertyName) == null ? null : dbEntry.CurrentValues.GetValue<object>(propertyName).ToString()
         }
         );
      }
 }

我得到的错误是 EntityEntry 不包含 OriginalValues 的定义。我要拔头发了...如何使用 EF 7 从修改后的对象中获取原始值?

【问题讨论】:

标签: c# .net entity-framework entity-framework-core


【解决方案1】:
// using System.Reflection;
foreach (var property in dbEntry.Entity.GetType().GetTypeInfo().DeclaredProperties)
{
    var originalValue = dbEntry.Property(property.Name).OriginalValue;
    var currentValue = dbEntry.Property(property.Name).CurrentValue;
    Console.WriteLine($"{property.Name}: Original: {originalValue}, Current: {currentValue}");
}

【讨论】:

  • 前缀$是什么语法? C# 6.0 的某种新特性?
  • @Hopeless,是的。这称为String Interpolation
  • 一如既往地建议进行错误处理。如果实体类包含模型中未包含的属性,dbEntry.Property(property.Name) 将抛出 InvalidOperationException,例如通过 [NotMapped] 注释,如 here 所示。如果您只想考虑模型中映射的属性,则可以改为循环 entry.Metadata.GetProperties()。这可能也适用于shadow properties,但我没有尝试。
【解决方案2】:

另一种选择是仍使用 OriginalValues,但使用 Properties 代替 PropertyNames。这将使 foreach 循环处理类型为 Microsoft.EntityFrameworkCore.Metadata.IPropertyGetValues 方法具有接受 IProperty 的重载,因此这些调用不需要更改代码,但您需要更改 ColumnName 分配从 propertyNameproperty名称

foreach (var property in entityEntry.OriginalValues.Properties)
{
    if (!object.Equals(entityEntry.OriginalValues.GetValue<object>(property), 
        entityEntry.CurrentValues.GetValue<object>(property)))
    {
        result.Add(
            new AuditLog()
            {
                UserId = userId,
                EventDate = changeTime,
                EventType = "M",
                TableName = tableName,
                RecordId = entityEntry.OriginalValues.GetValue<object>(keyName).ToString(),
                ColumnName = property.Name,
                OriginalValue =
                    entityEntry.OriginalValues.GetValue<object>(property) == null
                    ? null
                    : entityEntry.OriginalValues.GetValue<object>(property).ToString(),
                NewValue = 
                    entityEntry.CurrentValues.GetValue<object>(property) == null
                    ? null
                    : entityEntry.CurrentValues.GetValue<object>(property).ToString()
        });
    }
}

【讨论】:

    【解决方案3】:

    这可以通过这个方法 dbEntry.GetDatabaseValues() 解决。 示例代码如下所述。

     if (dbEntry.State == System.Data.EntityState.Modified)
    {
        foreach (string propertyName in dbEntry.OriginalValues.PropertyNames)
        {
        // Caputre current value 
         var originalValue = dbEntry.GetDatabaseValues().GetValue<object>(propertyName) == null ? null : dbEntry.GetDatabaseValues().GetValue<object> 
        (propertyName).ToString();
        // Caputre Updated value                    
        var currentValue = dbEntry.CurrentValues.GetValue<object>(propertyName) == null ? null : dbEntry.CurrentValues.GetValue<object>(propertyName).ToString();
            // For updates, we only want to capture the columns that actually changed
            if (!object.Equals(!object.Equals(originalValue, currentValue))
            {
                result.Add(new AuditLog()
                        {
                            AuditLogID = Guid.NewGuid(),
                            UserID = userId,
                            EventDateUTC = changeTime,
                            EventType = "M",    // Modified
                            TableName = tableName,
                            RecordID = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(),
                            ColumnName = propertyName,
                            OriginalValue = originalValue,
                            NewValue = currentValue,
                        }
                    );
            }
        }
    }
    

    【讨论】:

    • 坏主意。这将对通过此方法的 每个 实体进行数据库查询。对象图可以轻松包含数十个实体。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-29
    • 1970-01-01
    • 2016-04-16
    • 2020-06-29
    • 1970-01-01
    • 2019-11-20
    相关资源
    最近更新 更多