I am working on a .NET 6 application with EF Core. I have to implement Audit Trail in the project. One of the models of my project is:
public class SystemSpecification : AuditableEntity, IAggregateRoot
{
public int StudyType { get; set; }
public string StudyNumber { get; set; }
public string? Location { get; set; }
public string? Room { get; set; }
public string? ModelNumber { get; set; }
public string? SerialNumber { get; set; }
public virtual ICollection<SystemSpecificationSetPoint> SetPoints { get; set; }
}
SystemSpecificationSetPoint model is:
public class SystemSpecificationSetPoint : AuditableEntity, IAggregateRoot
{
public int Type { get; set; }
public string? Value { get; set; }
public string? AcceptableRange { get; set; }
public Guid SystemSpecificationId { get; set; }
public virtual SystemSpecification SystemSpecification { get; set; }
}
I am writing an HttpPut end point and need to Audit the values which are updated. My end point is :
public async Task<ApiResponse<bool>> Update(UpdateSystemSpecificationRequest request, CancellationToken cancellationToken)
{
var systemSpecificationToUpdate = await _dbContext.SystemSpecifications
.Where(x => x.Id == request.Id).FirstOrDefaultAsync();
_mapper.Map(request, systemSpecificationToUpdate);
_dbContext.SystemSpecifications.Update(systemSpecificationToUpdate);
await _dbContext.SaveChangesAsync(Guid.Parse("b8b8362e-adfd-4e2f-838b-e6440b60dab3"));
return new ApiResponse<bool>() { HasError = false, Result = true };
}
My SaveChangesAsync method is:
public virtual async Task<int> SaveChangesAsync(Guid? userId = null)
{
OnBeforeSaveChanges(userId);
var result = await base.SaveChangesAsync();
return result;
}
private void OnBeforeSaveChanges(Guid? userId)
{
ChangeTracker.DetectChanges();
var auditEntries = new List<AuditEntry>();
var changedEntities = ChangeTracker.Entries();
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);
auditEntry.TableName = entry.Entity.GetType().Name;
auditEntry.UserId = userId.Value;
auditEntries.Add(auditEntry);
foreach (var property in entry.Properties)
{
string propertyName = property.Metadata.Name;
if (property.Metadata.IsPrimaryKey() || property.Metadata.IsForeignKey())
{
auditEntry.KeyValues[propertyName] = property.CurrentValue;
continue;
}
switch (entry.State)
{
case EntityState.Added:
auditEntry.AuditType = Domain.Entities.Enums.AuditTypeEnum.Create;
auditEntry.NewValues[propertyName] = property.CurrentValue;
break;
case EntityState.Deleted:
auditEntry.AuditType = Domain.Entities.Enums.AuditTypeEnum.Delete;
auditEntry.OldValues[propertyName] = property.OriginalValue;
break;
case EntityState.Modified:
if (property.IsModified)
{
if (!string.Equals(property.OriginalValue?.ToString(), property.CurrentValue?.ToString()))
{
auditEntry.ChangedColumns.Add(propertyName);
auditEntry.AuditType = Domain.Entities.Enums.AuditTypeEnum.Update;
auditEntry.OldValues[propertyName] = property.OriginalValue;
auditEntry.NewValues[propertyName] = property.CurrentValue;
}
}
break;
}
}
}
foreach (var auditEntry in auditEntries)
{
Audit.Add(auditEntry.ToAudit());
}
}
Currently, the values in the SystemSpecificationSetPoints tables are:
I am changing the "Value" column values in the update API. I am sending the following payload to the HttpPut end point:
{
"id": "B6AA41F8-803A-428E-3035-08DB3FEBDA44",
"studyType": 1,
"studyNumber": "studyNumberUpdated",
"location": "locationUpdated",
"room": "room",
"modelNumber": "modelNumberUpdated",
"serialNumber": "serialNumberUpdated",
"equipmentTypeId": "4244B553-863E-401B-9331-08DB2ACFFFBF",
"equipmentClassification": 1,
"equipmentClassificationValue": "equipmentClassificationValueUpdated",
"setPoints": [
{
"Id": "15693D8B-CF4A-42EC-D784-08DB466504DF",
"type": 1,
"value": "-500",
"acceptableRange": "-50 to -100"
},
{
"Id": "288BAB37-BA86-4256-489F-08DB4BE559BD",
"type": 2,
"value": "-500",
"acceptableRange": "-50 to -100"
},
{
"Id": "58C87D4A-EAD9-4B2E-69E0-08DB4BE7AFB4",
"type": 3,
"value": "-500",
"acceptableRange": "-50 to -100"
}
],
"comment": "string",
"criteriaMet": 0
}
When I send this payload and look into the Original and Current values of the "Value" column (Child Table Column) in the change tracker, I get the updated value in both the Original and Current values. Because of this the EF does not consider this as change and I am unable to log it.
The Save call is not yet executed but the child table values are already updated. Can someone help me to resolve this issue. I believe I am missing something very minor/basic here but any help would me much appreciated.


