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

.NET Core微服务CQRS场景下BaseContext如何获取登录用户填充审计字段

EF Core BaseContext 审计字段自动填充当前用户方案

原有实现问题

你当前使用的Thread.CurrentPrincipal方式不适用于ASP.NET Core微服务场景:

  • ASP.NET Core默认不会给Thread.CurrentPrincipal赋值
  • 异步请求上下文切换时,线程关联的身份信息会丢失,大概率会拿到空值

推荐实现方案(基于ASP.NET Core原生认证体系)

第一步:注册HttpContext访问服务

在项目启动文件Program.cs中添加服务注册:

// 注册IHttpContextAccessor,用于访问当前请求的HttpContext
builder.Services.AddHttpContextAccessor();

第二步:修改BaseContext构造函数,注入HttpContext访问器

namespace LED.OM.Core.Base
{
    public abstract class BaseContext : DbContext
    {
        private readonly IHttpContextAccessor _httpContextAccessor;

        // 构造函数新增IHttpContextAccessor参数
        public BaseContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor) : base(options)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        // SaveChanges相关重写逻辑保持不变
        public override int SaveChanges()
        {
            UpdateAuditEntities();
            return base.SaveChanges();
        }

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

        public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            UpdateAuditEntities();
            return base.SaveChangesAsync(cancellationToken);
        }

        private void UpdateAuditEntities()
        {
            // 从HttpContext的用户Claims中获取身份信息,可根据项目认证逻辑调整取的Claim类型
            // 如果CreatedBy/UpdatedBy存用户名,就取ClaimTypes.Name;存用户ID就取ClaimTypes.NameIdentifier
            string currentUser = _httpContextAccessor.HttpContext?.User?.FindFirstValue(System.Security.Claims.ClaimTypes.NameIdentifier) ?? string.Empty;
            
            var modifiedEntries = ChangeTracker.Entries()
                .Where(x => x.Entity is Entity &&
                            (x.State == EntityState.Added || x.State == EntityState.Modified || x.State == EntityState.Deleted));

            foreach (var entry in modifiedEntries)
            {
                var entity = (Entity)entry.Entity;
                var now = DateTime.UtcNow;

                if (entry.State == EntityState.Added)
                {
                    entity.CreatedDate = now;
                    entity.CreatedBy = currentUser;
                }
                else if (entry.State == EntityState.Deleted && entry.Entity is not IHardDelete)
                {
                    entry.State = EntityState.Unchanged;
                    entity.IsDelete = true;
                }
                else
                {
                    Entry(entity).Property(x => x.CreatedBy).IsModified = false;
                    Entry(entity).Property(x => x.CreatedDate).IsModified = false;
                }

                entity.UpdatedDate = now;
                entity.UpdatedBy = currentUser;
            }
        }
    }
}

兼容无HttpContext场景(可选)

如果你的BaseContext需要在后台定时任务、MQ消费者等没有HTTP请求的场景使用,可以进一步封装ICurrentUser服务,兼容多场景:

1. 定义用户访问接口

public interface ICurrentUser
{
    /// <summary>
    /// 当前登录用户ID
    /// </summary>
    string UserId { get; }
    /// <summary>
    /// 当前登录用户名
    /// </summary>
    string UserName { get; }
    /// <summary>
    /// 是否已经认证
    /// </summary>
    bool IsAuthenticated { get; }
}

2. 实现接口

public class CurrentUser : ICurrentUser
{
    private readonly IHttpContextAccessor _httpContextAccessor;

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

    public string UserId => _httpContextAccessor.HttpContext?.User?.FindFirstValue(System.Security.Claims.ClaimTypes.NameIdentifier) ?? string.Empty;
    public string UserName => _httpContextAccessor.HttpContext?.User?.FindFirstValue(System.Security.Claims.ClaimTypes.Name) ?? string.Empty;
    public bool IsAuthenticated => !string.IsNullOrEmpty(UserId);
}

3. 注册服务

builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ICurrentUser, CurrentUser>();

4. 注入BaseContext使用

public BaseContext(DbContextOptions options, ICurrentUser currentUser) : base(options)
{
    _currentUser = currentUser;
}
// 后续直接从_currentUser.UserId取值即可

注意事项

  • 确保项目已经正确配置认证中间件,Program.cs中存在app.UseAuthentication()和app.UseAuthorization(),且顺序位于路由配置之后、端点映射之前
  • 如果你的项目使用自定义Claim存储用户ID/用户名,将FindFirstValue的参数替换为你的自定义Claim键即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 06:57:00