Search code examples
c#.net-coreasp.net-core-6.0audit.net

Setting up Audit.EntityFramework.Core for basic auditing


In .net core web app I was using Audit.EntityFramework 16.1.1 with .net core 3.1 to log changes for two purposes:

  • logging changes for specific tables for history/security and
  • logging all the changes into one table (which is periodically deleted by scheduled job), for some rudimentary forensics if needed.

Code used in Startup.cs is like this:

var currentUser = app.ApplicationServices.GetRequiredService<ICurrentUser>();

Audit.Core.Configuration.Setup()
    .UseEntityFramework(ef => ef
        .AuditTypeMapper(t => typeof(AuditLog))
        .AuditEntityAction<AuditLog>((ev, entry, entity) =>
        {
            entity.Id = Guid.NewGuid();
            entity.AuditData = entry.ToJson();
            entity.EntityType = entry.EntityType.Name;
            entity.AuditDate = DateTime.Now;
            entity.AuditUsername = currentUser.GetUsername();
            entity.AuditUserId = currentUser.GetUserId();
            entity.EntityId = entry.PrimaryKey.Count > 1 ?
                string.Join("|", entry.PrimaryKey.Select(pk => pk.Value)) :
                entry.PrimaryKey.First().Value.ToString();
            entity.Action = entry.Action;
        })
        .IgnoreMatchedProperties(true));

Audit.Core.Configuration.Setup()
    .UseEntityFramework(ef => ef
        .AuditTypeExplicitMapper(m => m
        .Map<Table1, Table1History>((table, tableHistory) =>
        {
            FillTableHistoryData(table, tableHistory);
        })
        .Map<Table2, Table2History>((otherTable, otherTableHistory) =>
        {
            FillOtherTableHistoryData(otherTable, otherTableHistory);
        })

After upgrading app to .net core 6 and Audit.EntityFramework to 19.1.0 (or any newer version) this stopped working. Reverting back to 16.1.1. throws MethodNotFoundException (for GetTableName method).

So, I would like to avoid some serious refactoring and make this work as it used to at the same time. Could someone with experience with this give me a push in the right direction?


Solution

  • I'm not sure why it used to work in the old version, but you shouldn't call .UseEntityFramework() multiple times since it will override the settings each time.

    One option is to use .MapExplicit within the AuditTypeExplicitMapper to handle the tables not included in the .Map<> calls.

    For example:

    Audit.Core.Configuration.Setup()
        .UseEntityFramework(ef => ef
            .AuditTypeExplicitMapper(m => m
                .Map<Table1, Table1History>((table, tableHistory) =>
                {
                    FillTableHistoryData(table, tableHistory);
                })
                .Map<Table2, Table2History>((otherTable, otherTableHistory) =>
                {
                    FillOtherTableHistoryData(otherTable, otherTableHistory);
                })
                .MapExplicit<AuditLog>(entry => IsAnotherTable(entry), (entry, entity) =>
                {
                    entity.Id = Guid.NewGuid();
                    entity.AuditData = entry.ToJson();
                    entity.EntityType = entry.EntityType.Name;
                    // ...
                }))
            .IgnoreMatchedProperties(true));
    

    You need to specify which entities are going to be handled by the .MapExplicit, for example:

    bool IsAnotherTable(EventEntry entry)
    {
        return entry.GetEntry().Entity is not Table1 and not Table2;
    }