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

Asp.Net Core 5中无需从Controller传参,如何在审计扩展中获取AppUser

方案1:通过服务定位器在扩展方法内获取AppUser(不推荐)

静态扩展方法本身没有直接访问DI容器的能力,硬要实现可以用服务定位器模式:

  1. 首先在Startup.cs/Program.cs中注册HttpContext访问服务:
services.AddHttpContextAccessor();
  1. 定义全局静态服务提供者,启动时将容器赋值给该变量:
// 放在公共静态类中
public static class GlobalServiceProvider
{
    public static IServiceProvider Instance { get; set; }
}

// Startup的Configure方法中赋值
public void Configure(IApplicationBuilder app)
{
    GlobalServiceProvider.Instance = app.ApplicationServices;
    // 其他启动逻辑
}
  1. 改造扩展方法直接获取当前用户:
public static void WriteCreationAudit(this IAuditProperties obj)
{
    var httpAccessor = GlobalServiceProvider.Instance.GetRequiredService<IHttpContextAccessor>();
    var userManager = GlobalServiceProvider.Instance.GetRequiredService<UserManager<AppUser>>();
    var userId = httpAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier);
    var currentUser = userId == null ? null : userManager.FindByIdAsync(userId).GetAwaiter().GetResult();
    
    obj.CreatedBy = currentUser?.FullName ?? "System";
    obj.CreatedOn = DateTime.Now.InMorocco();
    obj.IsEdited = false;
}

该方案缺点明显:违反依赖注入的显式依赖原则,可测试性差,非HTTP场景(如后台任务)下HttpContext为空会触发异常,仅做参考不建议生产使用。


方案2:重写EF Core的SaveChanges方法(最推荐)

这是ASP.NET Core中实现实体审计的标准方案,完全不需要Controller层参与,审计逻辑在数据持久化层自动处理,对业务代码透明。
实现步骤:

  1. 给自定义DbContext注入依赖:
public class AppDbContext : IdentityDbContext<AppUser>
{
    private readonly IHttpContextAccessor _httpAccessor;
    private readonly UserManager<AppUser> _userManager;

    public AppDbContext(DbContextOptions<AppDbContext> options,
        IHttpContextAccessor httpAccessor,
        UserManager<AppUser> userManager) : base(options)
    {
        _httpAccessor = httpAccessor;
        _userManager = userManager;
    }
}
  1. 重写SaveChanges系列方法,自动处理审计字段赋值:
public override int SaveChanges()
{
    ProcessAuditProperties();
    return base.SaveChanges();
}

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

private void ProcessAuditProperties()
{
    var userId = _httpAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier);
    AppUser currentUser = null;
    if (userId != null)
    {
        currentUser = _userManager.FindByIdAsync(userId).GetAwaiter().GetResult();
    }
    // 过滤所有新增/修改的审计实体
    var auditEntries = ChangeTracker.Entries<IAuditProperties>()
        .Where(e => e.State is EntityState.Added or EntityState.Modified);

    foreach (var entry in auditEntries)
    {
        var entity = entry.Entity;
        if (entry.State == EntityState.Added)
        {
            entity.CreatedBy = currentUser?.FullName ?? "System";
            entity.CreatedOn = DateTime.Now.InMorocco();
            entity.IsEdited = false;
        }
        else
        {
            entity.IsEdited = true;
            entity.LastEditor = currentUser?.FullName ?? "System";
            entity.LastEditDate = DateTime.Now.InMorocco();
            // 可选:强制禁止修改创建信息字段
            entry.Property(x => x.CreatedBy).IsModified = false;
            entry.Property(x => x.CreatedOn).IsModified = false;
        }
    }
}
  1. 确保注册了IHttpContextAccessor服务:
services.AddHttpContextAccessor();

优化提示:可以在用户登录时将FullName写入Claims,无需每次查询UserManager,直接从HttpContext.User的Claims中读取即可,减少数据库请求。

该方案优势:

  • 完全解耦审计逻辑和业务逻辑,不需要在Controller或业务层做任何审计相关操作
  • 遵循依赖注入原则,可测试性好
  • 支持非HTTP场景,后台任务执行时可以自定义默认系统用户信息
  • 避免遗漏审计字段赋值,所有实体变更都会自动触发审计逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 23:45:01