Reputation: 317
I have a issue in EF7/asp.Net Core application. In my context I create a method Save:
public int Save()
{
ChangeTracker.DetectChanges();
var modifiedEntities = ChangeTracker.Entries()
.Where(p => p.State == EntityState.Modified || p.State == EntityState.Added || p.State == EntityState.Deleted || p.State == EntityState.Modified || p.State == EntityState.Detached).ToList();
var now = DateTime.UtcNow;
foreach (var change in modifiedEntities)
{
var entityName = change.Entity.GetType().Name;
var primaryKeyValue = GetPrimaryKeyValue(change.Entity);
foreach (var prop in change.Entity.GetType().GetTypeInfo().DeclaredProperties)
{
if (!prop.GetGetMethod().IsVirtual)
{
var currentValue = change.Property(prop.Name).CurrentValue;
var originalValue = change.Property(prop.Name).OriginalValue;
if (originalValue.ToString() != currentValue.ToString())
{
var changeLoged = new ChangeLog
{
PropertyName = prop.Name,
EntityName = entityName,
PrimaryKeyValue = primaryKeyValue,
DateChange = now,
OldValue = originalValue.ToString(),
NewValue = currentValue.ToString(),
ChangedBy = "test"
};
ChangeLog.Add(changeLoged);
}
}
}
}
return base.SaveChanges();
}
and method GetPrimaryKeyValue:
protected virtual int GetPrimaryKeyValue<T>(T entity)
{
var test = entity;
var test2 = test.GetType();
var keyName = this.Model.FindEntityType(test2).FindPrimaryKey().Properties
.Select(x => x.Name).Single();
var result = (int)entity.GetType().GetProperty(keyName).GetValue(entity, null);
if (result < 0)
return -1;
return result;
}
Unfortunatlly the change.Property(prop.Name).CurrentValue always equals OriginalValue, so the if
originalValue.ToString() != currentValue.ToString()
always return false.
Upvotes: 7
Views: 9354
Reputation: 439
Based on the answers above I refactored my SetChanges method to this, and its working fine now! (EF 6 and .NET 6)
private void SetChanges()
{
TableName = Entry.Metadata.GetTableName();
var entsInDB = Entry.GetDatabaseValues();
foreach (PropertyEntry property in Entry.Properties)
{
if (property != null)
{
string propertyName = property.Metadata.Name;
if (property.Metadata.IsPrimaryKey())
{
KeyValues[propertyName] = property.CurrentValue ?? "";
continue;
}
switch (Entry.State)
{
case EntityState.Added:
NewValues[propertyName] = property.CurrentValue ?? "";
AuditType = AuditType.Create;
break;
case EntityState.Deleted:
OldValues[propertyName] = property.OriginalValue ?? "";
AuditType = AuditType.Delete;
break;
case EntityState.Modified:
if (property.IsModified)
{
var originalValue = entsInDB?.GetValue<object>(property.Metadata.Name);
if (originalValue?.ToString() != property.CurrentValue?.ToString())
{
ChangedColumns.Add(propertyName);
OldValues[propertyName] = originalValue?.ToString() ?? "";
NewValues[propertyName] = property.CurrentValue ?? "";
AuditType = AuditType.Update;
}
}
break;
}
}
}
}
Logging it to the database audit table:
Upvotes: 3
Reputation: 161
Replace:
var originalValue = change.Property(prop.Name).OriginalValue;
to:
var originalValue = change.GetDatabaseValues().GetValue<object>(prop.Name);
Upvotes: 15
Reputation: 11347
This will not exactly answer your question since I cannot reproduce this issue but this may help you.
In EF Core, the PropertyEntry class has now an IsModified property which let you know if the value has been modified or not.
You should use it instead:
if (change.Property(prop.Name).IsModified)
{
var changeLoged = new ChangeLog
{
PropertyName = prop.Name,
EntityName = entityName,
PrimaryKeyValue = primaryKeyValue,
DateChange = now,
OldValue = originalValue.ToString(),
NewValue = currentValue.ToString(),
ChangedBy = "test"
};
ChangeLog.Add(changeLoged);
}
Disclaimer: I'm the owner of the project Entity Framework Plus
The Library has an Audit Feature (Supporting EF Core) which you may use or get inspired by to create your auditing (The code is Open Source).
Documentation: EF+ Audit
Upvotes: 3