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

ASP.NET Core Web API:SaveChangesAsync中Current UserName为空问题排查

问题原因及修复方案

核心原因

问题出在依赖注入生命周期不匹配或UnitOfWork实现错误,导致ApplicationDbContext中注入的ICurrentUserService无法获取当前请求的用户上下文:

  • 如果UnitOfWork或ApplicationDbContext被注册为Singleton生命周期,它们会在应用启动时就被实例化,而ICurrentUserService依赖的HttpContext是请求级的,Singleton实例无法随请求更新用户信息。
  • 若UnitOfWork自行创建ApplicationDbContext实例(而非通过DI注入),新创建的DbContext会拿到没有绑定HttpContext的ICurrentUserService实例,自然获取不到用户信息。
  • 少数情况是ICurrentUserService的实现存在问题,比如缓存了用户信息,没有实时从HttpContext获取。

修复步骤

1. 统一服务生命周期

确保所有相关服务都注册为Scoped(请求级),因为它们都依赖当前请求的HttpContext:

// 注册HttpContextAccessor(必须)
builder.Services.AddHttpContextAccessor();
// 注册CurrentUserService为Scoped
builder.Services.AddScoped<ICurrentUserService, CurrentUserService>();
// 注册DbContext为Scoped(默认就是Scoped,可显式声明)
builder.Services.AddDbContext<ApplicationDbContext>(options => 
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// 注册UnitOfWork为Scoped
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();

2. 修正UnitOfWork实现

确保UnitOfWork通过构造函数注入ApplicationDbContext,而非自行实例化:

public class UnitOfWork : IUnitOfWork
{
    private readonly ApplicationDbContext _dbContext;

    // 通过DI注入DbContext,保证拿到的是当前请求的Scoped实例
    public UnitOfWork(ApplicationDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public void Save()
    {
        _dbContext.SaveChanges();
    }

    public async Task SaveAsync()
    {
        await _dbContext.SaveChangesAsync();
    }
}

3. 确保CurrentUserService正确从HttpContext获取信息

检查ICurrentUserService的实现,确保实时从当前请求的HttpContext读取用户信息,而非缓存:

public class CurrentUserService : ICurrentUserService
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CurrentUserService(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    // 实时从HttpContext获取,每次调用都会取当前请求的用户信息
    public string? UserId => _httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier);
    public string? UserName => _httpContextAccessor.HttpContext?.User.Identity?.Name;
}

4. 验证DbContext的审计逻辑

确保重写SaveChanges/SaveChangesAsync时,每次都会重新获取ICurrentUserService的用户信息,而非提前缓存:

public class ApplicationDbContext : IdentityDbContext
{
    private readonly ICurrentUserService _currentUserService;

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, ICurrentUserService currentUserService)
        : base(options)
    {
        _currentUserService = currentUserService;
    }

    public override int SaveChanges(bool acceptAllChangesOnSuccess)
    {
        UpdateAuditableEntities();
        return base.SaveChanges(acceptAllChangesOnSuccess);
    }

    public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
    {
        UpdateAuditableEntities();
        return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
    }

    private void UpdateAuditableEntities()
    {
        // 每次保存时实时获取当前用户名,避免缓存旧值
        var currentUserName = _currentUserService.UserName;
        var now = DateTime.UtcNow;

        foreach (var entry in ChangeTracker.Entries<AuditableEntity>())
        {
            switch (entry.State)
            {
                case EntityState.Added:
                    entry.Entity.CreatedBy = currentUserName;
                    entry.Entity.CreatedAt = now;
                    entry.Entity.LastModifiedBy = currentUserName;
                    entry.Entity.LastModifiedAt = now;
                    break;
                case EntityState.Modified:
                    entry.Entity.LastModifiedBy = currentUserName;
                    entry.Entity.LastModifiedAt = now;
                    // 禁止修改创建信息
                    entry.Property(x => x.CreatedBy).IsModified = false;
                    entry.Property(x => x.CreatedAt).IsModified = false;
                    break;
            }
        }
    }
}

5. 非请求场景的特殊处理

如果需要在后台任务、定时任务等非请求上下文调用UnitOfWork,需要手动创建服务范围,并手动设置用户信息(如果需要审计):

using var scope = app.Services.CreateScope();
var unitOfWork = scope.ServiceProvider.GetRequiredService<IUnitOfWork>();
// 若需要审计,可手动注入一个模拟的CurrentUserService,或者修改DbContext支持手动设置用户信息
await unitOfWork.SaveAsync();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:31:34