efcore icon indicating copy to clipboard operation
efcore copied to clipboard

Get PrimaryKey value in SavingChangesAsync interceptor

Open prrami opened this issue 1 year ago • 1 comments

I have implemented below code in SavingChangesAsync interceptor. I want to implement AuditLogs for each entry/modification in table. How to get primaryKeyValue in case of entry.state == Added. How can I get newly generated Id. Right now I am getting some random value (-9223372036854774807) in case of entry.state==Added.

I have tried to do it with SavedChangesASync also but there I get entry.state as Unchanged always even if entry is Added or Modified.

public override async ValueTask<InterceptionResult<int>> SavingChangesAsync(
DbContextEventData eventData,
InterceptionResult<int> result,
CancellationToken cancellationToken = default)
{

    foreach (var entry in eventData.Context.ChangeTracker.Entries())
    {
        if (entry.State == EntityState.Added ||
            entry.State == EntityState.Modified ||
            entry.State == EntityState.Deleted)
        {
            var primaryKeyValue = GetPrimaryKeyValue(entry);

            var auditLog = new AuditLog
            {
                TableName = entry.Metadata.GetTableName(),
                TablePrimaryKey = primaryKeyValue,
                Action = GetAction(entry.State),
                OldValue = entry.State == EntityState.Added ? null : JsonConvert.SerializeObject(entry.OriginalValues.ToObject()),
                NewValue = JsonConvert.SerializeObject(entry.Entity),
                AuditDate = DateTime.UtcNow,
                AuditUsername = _userEmail
            };

            _auditLogsDbContext.AuditLogs.Add(auditLog);
        }

    }

    // Call the base interceptor method
    return await base.SavingChangesAsync(eventData, result, cancellationToken);
}

private string GetPrimaryKeyValue(EntityEntry entry)
{
    var primaryKey = entry.Metadata.FindPrimaryKey();
    var primaryKeyProperty = primaryKey.Properties.FirstOrDefault();
    return entry.Property(primaryKeyProperty.Name).CurrentValue.ToString();
}

prrami avatar May 07 '24 17:05 prrami

@prrami Store generated key values are not available until the database has generated these values. This means they will never be available in SavingChangesAsync, since this is called before SQL is sent to the database. They are available in SavedChangesAsync, but it's currently not easy to know the original state of each entity at that point--you will have to maintain your own map, or use events that trigger when the state changes. Improving this is tracked by #32373.

ajcvickers avatar May 08 '24 12:05 ajcvickers