vick
vick

Reputation: 81

Audit.NET Adding UserName to AuditLog when logging EF but can't reach HttpContext

Context

I am trying wireup Audit.Net in an MVC5 app with .Net Framework 4.8.

Dataprovider: EntityFramework

Output: Cosmos

DI Provider: Autofac

The Problem

I have tried the following call backs to try and write the username to the AuditEvent.

Configuration.AddOnCreatedAction(scope =>
{
    var username = HttpContext.Current.User.Identity.GetUserId();
    scope.SetCustomField("User2", username);
});
Configuration.AddOnSavingAction(scope =>
{
    var username = HttpContext.Current.User.Identity.GetUserId();
    scope.SetCustomField("User2", username);
});
Configuration.AddOnSavingAction(scope =>
{
    var user = AutofacDependencyResolver.Current.Resolve<IPrincipal>();
    scope.SetCustomField("User2", user.Identity.GetUserId());
});

These for work for synchronous calls to dbContext.SaveChanges(), when SaveChangesAsync() is called then I cannot access the current HttpContext as it is always null.

Any suggestions?

Upvotes: 2

Views: 1152

Answers (2)

Edward Olamisan
Edward Olamisan

Reputation: 891

You may capture the HttpContext earlier in the Asp .Net pipeline:

public interface IHttpContextContainer
{
    HttpContextBase HttpContextBase { get; set; }
}

In your controller or service you may attach the lifetime scope to your custom Audit event:

public abstract class MyAuditEvent : AuditEvent
{
    [JsonIgnore]
    public ILifetimeScope LifetimeScope { get; set; }
}

Add JsonIgnoreAttribute to prevent serializing the LifetimeScope.

In the global event handler you may now access the custom audit event:

Configuration
            .AddCustomAction(
                ActionType.OnScopeCreated,
                auditScope =>
                {
                    if (auditScope.Event is MyAuditEvent auditEvent)
                    {
                        var scope = auditEvent.LifetimeScope;
                        var httpContextContainer = scope.Resolve<IHttpContextContainer>();
                        // ...
                        // access HttpContextBase from httpContextContainer
                    }
                });

Upvotes: 0

Travis Illig
Travis Illig

Reputation: 23924

The key (which might help in search terms) is async. A quick search for async httpcontext mvc gets us to this existing question with a lot of information about async/await and HttpContext usage.

The super short version: in MVC, the HttpContext gets carried around in the thread synchronization context (which is also where, say, the culture and other thread settings are carried around) but it's expensive to cart that context from thread to thread so the default is to not do that. You need to enable it explicitly to work.

Here's a blog article explaining the various knobs you can turn in web.config to get it to work but the net result is, basically, to make sure <httpRuntime targetFramework="4.5" /> is set (well, set that value to 4.5 or higher).

If that's already set, then... maybe there's something else at play, like a call has the ConfigureAwait(false) set on it so it's not returning to a thread context that has the HttpContext. But, more than likely, that httpRuntime flag should fix it.

Upvotes: 1

Related Questions