You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Audit.NET记录EF日志时调用SaveChangesAsync无法获取HttpContext添加用户名

问题根因

.NET Framework 下 HttpContext.Current 基于线程静态存储实现,调用 SaveChangesAsync() 触发异步流程时,await 操作会导致线程切换到线程池线程,原始请求的同步上下文不会默认携带到新线程,因此回调中读取 HttpContext.Current 始终为 null。同理 AutofacDependencyResolver.Current 依赖请求级 DI 作用域,异步线程中也无法解析到请求对应的 IPrincipal 实例。

可行解决方案

方案1:提前注入用户信息(最稳定,推荐)

不要在Audit的全局回调中读取请求上下文,提前在请求上下文有效的阶段把用户ID写入审计上下文:

  1. 首先修改你自定义的AuditDbContext,新增字段存储当前用户ID,构造函数注入IPrincipal(Autofac注册DbContext时需设为InstancePerRequest,IPrincipal也注册为请求作用域实例):
public class YourAuditDbContext : AuditDbContext
{
    public string CurrentUserId { get; }
    public YourAuditDbContext(IPrincipal principal)
    {
        CurrentUserId = principal.Identity.GetUserId();
    }
}
  1. 配置Audit.EntityFramework从DbContext实例读取用户信息,无需再用全局的AddOnSavingAction回调:
Audit.EntityFramework.Configuration.Setup()
    .ForContext<YourAuditDbContext>(cfg => 
        cfg.AuditEntityAction = (auditEvent, auditEntity, dbContext) => 
        {
            var db = (YourAuditDbContext)dbContext;
            auditEvent.CustomFields["User2"] = db.CurrentUserId;
        });

如果不想改DbContext,也可以写全局Action过滤器,在Action执行前(此时HttpContext有效)提前写入AuditScope自定义字段:

public class AuditUserFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var userId = filterContext.HttpContext.User.Identity.GetUserId();
        if (AuditScope.Current != null)
        {
            AuditScope.Current.SetCustomField("User2", userId);
        }
    }
    public void OnActionExecuted(ActionExecutedContext filterContext) {}
}

方案2:逻辑调用上下文传递用户ID

利用.NET Framework的逻辑调用上下文异步流传播特性存储用户ID,不受线程切换影响:

  1. 在全局.asax的Application_AuthenticateRequest事件中存入用户ID:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    if (User?.Identity != null && User.Identity.IsAuthenticated)
    {
        CallContext.LogicalSetData("CurrentAuditUserId", User.Identity.GetUserId());
    }
}
// 请求结束时清理,避免内存泄漏
protected void Application_EndRequest(object sender, EventArgs e)
{
    CallContext.FreeNamedDataSlot("CurrentAuditUserId");
}
  1. 全局回调中直接读取逻辑上下文的用户ID:
Configuration.AddOnSavingAction(scope =>
{
    var userId = CallContext.LogicalGetData("CurrentAuditUserId") as string;
    scope.SetCustomField("User2", userId);
});

方案3:配置异步上下文保留(仅作为备选,不稳定)

确保web.config配置正确,让运行时保留异步同步上下文:

<system.web>
  <httpRuntime targetFramework="4.8" useTaskFriendlySynchronizationContext="true" />
</system.web>

该方案依赖整个异步调用链没有使用ConfigureAwait(false),否则仍然会丢失上下文,仅作为临时兼容方案使用。


内容的提问来源于stack exchange,提问作者vick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 13:54:05