Audit.NET 在记录 EF 但无法访问 HttpContext 时将 UserName 添加到 AuditLog

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

上下文

我正在使用 .Net Framework 4.8 在 MVC5 应用程序中尝试接线 Audit.Net。

数据提供者:EntityFramework

输出:Cosmos

DI 提供商:Autofac

问题

我尝试了以下回调来尝试将用户名写入 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());
});

这些用于同步调用 dbContext.SaveChanges(),当调用 SaveChangesAsync() 时,我无法访问当前的 HttpContext,因为它始终为 null。

有什么建议吗?

关键字(可能有助于搜索字词)是 async。快速搜索 async httpcontext mvc 让我们找到 ,其中包含大量关于 async/awaitHttpContext 用法的信息。

超短版本:在 MVC 中,HttpContext 在线程同步上下文中被携带(这也是文化和其他线程设置被携带的地方)但是购物很昂贵该上下文从一个线程到另一个线程,因此默认设置是 不这样做 您需要明确启用它才能工作。

Here's a blog article explaining the various knobs you can turn in web.config to get it to work 但最终结果基本上是确保 <httpRuntime targetFramework="4.5" /> 已设置(好吧,将该值设置为 4.5 或更高 )。

如果 已经 设置了,那么...也许还有其他原因,比如呼叫设置了 ConfigureAwait(false) 所以它不会返回到具有 HttpContext 的线程上下文。但是,更有可能的是,httpRuntime 标志应该可以修复它。

您可以在 Asp .Net 管道中更早地捕获 HttpContext:

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

在您的控制器或服务中,您可以将生命周期范围附加到您的自定义审计事件:

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

添加 JsonIgnoreAttribute 以防止序列化 LifetimeScope。

在全局事件处理程序中,您现在可以访问自定义审计事件:

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