I have an audit table that tracks Added, Deleted and Modified. I track this inside Entity Framework instead of using a Database trigger for multiple reasons but really because we use a Process Account and I want to track what user physically made that change to that record.
I had this working in with EF 5 & I cannot remember I might have had it working in EF6 as well. Either way I am having the hardest time with EF 7 trying to capture the original values.
I noticed that when I am in the watch - I can see Original Values inside the Non-public members - so in my head I know it has to exist somewhere.
Ultimately this works inside EF earlier versions:
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()
}
);
}
}
The error I am getting is the EntityEntry does not contain a defition for OriginalValues. I am going to pull my hair out... How do I get the Original Values from a Modified Object with EF 7?
// 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}");
}
Another option is to still use OriginalValues, but instead of PropertyNames, use Properties. This will make the foreach loop process types of Microsoft.EntityFrameworkCore.Metadata.IProperty. The GetValues method has an overload that accepts IProperty, so no changes needed in code for those calls, but you will need to change the ColumnName assignment from propertyName to property.Name.
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()
});
}
}