Search code examples
c#entity-frameworkentity-framework-6savechangesaudit-logging

How to get id from entity for Auditlog in Entity Framework 6


I know it's several similar posts out there, but I cannot find any with a solution to this issue.

I want to add a (sort of) AudioLog when adding, changing or deleting entities (soft-delete) in Entity Framework 6. I've overridden the SaveChanges and because I only want to add log entries for EntityStates Added, Modified or Deleted, I fetch the list before I call SaveChanges the first time. The problem is, because I need to log what operation has been executed, I need to inspect the EntityState of the entities. But after SaveChanges is called, the EntityState is Unchanged for all entries.

public override int SaveChanges()
{
    using (var scope = new TransactionScope())
    {
        var modifiedEntries = ChangeTracker.Entries()
            .Where(e => e.State == EntityState.Added || e.State == EntityState.Deleted || e.State == EntityState.Modified)
            .ToList();

        int changes = base.SaveChanges();
        foreach (var entry in modifiedEntries)
        {
            ApplyAuditLog(entry);
        }

        base.SaveChanges();
        scope.Complete();
        return changes;
    }
}

private void ApplyAuditLog(DbEntityEntry entry)
{
    ILog entity = entry.Entity as ILog;

    if (entity != null)
    {
        LogOperation operation;
        switch (entry.State)
        {
            case EntityState.Added:
                operation = LogOperation.CreateEntity;
                break;
            case EntityState.Deleted:
                operation = LogOperation.DeleteEntity;
                break;
            case EntityState.Modified:
                operation = LogOperation.UpdateEntity;
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }

        AuditLog log = new AuditLog
        {
            Created = DateTime.Now,
            Entity = entry.Entity.GetType().Name,
            EntityId = entity.Id,
            Operation = operation,
        };

        AuditLog.Add(log);
    }
}

Solution

  • Ahhh... Off course!! The id will only be a "problem" for the entities that are newly added, so by splitting the list into two (one for modified/deleted and one for added), I create the AuditLog in two stages.

    For anyone else who want to apply this kind of AuditLog, here is my working code:

    public override int SaveChanges()
    {
        using (var scope = new TransactionScope())
        {
            var addedEntries = ChangeTracker.Entries().Where(e => e.State == EntityState.Added).ToList();
            var modifiedEntries = ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted || e.State == EntityState.Modified).ToList();
    
            foreach (var entry in modifiedEntries)
            {
                ApplyAuditLog(entry);
            }
    
            int changes = base.SaveChanges();
            foreach (var entry in addedEntries)
            {
                ApplyAuditLog(entry, LogOperation.CreateEntity);
            }
    
            base.SaveChanges();
            scope.Complete();
            return changes;
        }
    }
    
    private void ApplyAuditLog(DbEntityEntry entry)
    {
        LogOperation operation;
        switch (entry.State)
        {
            case EntityState.Added:
                operation = LogOperation.CreateEntity;
                break;
            case EntityState.Deleted:
                operation = LogOperation.DeleteEntity;
                break;
            case EntityState.Modified:
                operation = LogOperation.UpdateEntity;
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
    
        ApplyAuditLog(entry, operation);
    }
    
    private void ApplyAuditLog(DbEntityEntry entry, LogOperation logOperation)
    {
        ILog entity = entry.Entity as ILog;
    
        if (entity != null)
        {
            AuditLog log = new AuditLog
            {
                Created = DateTime.Now,
                Entity = entry.Entity.GetType().Name,
                EntityId = entity.Id,
                Operation = logOperation,
            };
            AuditLog.Add(log);
        }
    }