Child entity showing updated value in the OriginalValue property in the Entity Framework Core Audit Trail

99 Views Asked by At

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:

enter image description here

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.

enter image description here

enter image description here

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.

0

There are 0 best solutions below