Search code examples
c#.netentity-frameworkentity-framework-coreaudit.net

Audit.Net Get the name of the method which called .SaveChangesAsync()


I have the following Setup

Audit.Core.Configuration.Setup() // Triggered on a database save.
    .UseEntityFramework(ef => ef
        .AuditTypeExplicitMapper(m => m
            .Map<Item, AuditLog>()
            .AuditEntityAction<AuditLog>((evt, entry, auditEntity) =>
            {
                auditEntity.AuditData = entry.ToJson();
                auditEntity.AuditDate = DateTime.UtcNow;
                auditEntity.CallingMethod = "Calling Method"; <-- Where I need the calling method.
            })
        )
    );
    public async Task DoSomeWork() {  //<--- I want to get "DoSomeWork"
        // Some work..
        await dbConn.SaveChangesAsync();
        return updated;
    }

How can I get access to the method which called .saveChangesAsync so I know where it was triggered from? I've checked using StackTrace however that is flooded with library level frames which are not very helpful and I can't find the correct parent method.

I see in evt there is a that it provides a calling parent name, however that is just one level up so is just the "SaveChangesAsync" method.

Thanks


Solution

  • If you can change the code that calls SaveChangesAsync and you are inheriting your DbContext from AuditDbContext (High-Level SaveChanges interception), then you could add a new SaveChanges method with an optional parameter decorated with [CallerMemberName] and store the caller member name in a custom field of the AuditEvent.

    For example:

    public class YourDbContext : AuditDbContext
    {
        public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) 
            => throw new NotImplementedException("Should call SaveChangesAuditAsync");
    
        public Task<int> SaveChangesAuditAsync([CallerMemberName] string memberName = "", CancellationToken cancellationToken = default)
        {
            this.AddAuditCustomField("Caller", memberName);
            return base.SaveChangesAsync(cancellationToken);
        }
    }
    

    So, in your AuditEntityAction you can get the caller member name from the custom field:

        ...
        .AuditEntityAction<AuditLog>((evt, entry, auditEntity) =>
        {
            auditEntity.CallingMethod = evt.CustomFields["Caller"];
        })
    

    Now your DoSomeWork() must call the new method:

    public async Task DoSomeWork() 
    {
        ...
        await dbConn.SaveChangesAuditAsync();
        return updated;
    }