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

Blazor中如何通过依赖注入在EF Core中获取ClaimsPrincipal?

Blazor中如何通过依赖注入在EF Core中获取ClaimsPrincipal?

我太懂你这种困扰了——明明想在EF Core层面自动搞定审计字段的填充,结果把AuthenticationStateProvider直接注入DbContext后就报错,只能退而求其次在Blazor组件里手动设置,确实有点烦。其实核心问题在于DbContext的生命周期和Blazor的认证上下文不同步,直接注入会导致在认证状态还没初始化的时候就去拿用户信息,自然会出错。

给你分享一个靠谱的解决方案,既符合分层架构,又能自动填充审计字段:

第一步:创建一个专门的用户上下文服务

不要直接在DbContext里用AuthenticationStateProvider,咱们先封装一个专门的服务来获取当前用户信息,这样能更好地处理生命周期问题。

首先定义一个接口:

public interface IUserContext
{
    Task<ClaimsPrincipal> GetCurrentUserAsync();
}

然后实现这个接口:

public class UserContext : IUserContext
{
    private readonly AuthenticationStateProvider _authStateProvider;
    private ClaimsPrincipal _cachedUser;

    public UserContext(AuthenticationStateProvider authStateProvider)
    {
        _authStateProvider = authStateProvider;
    }

    public async Task<ClaimsPrincipal> GetCurrentUserAsync()
    {
        // 缓存用户信息,避免重复调用认证服务
        if (_cachedUser != null)
            return _cachedUser;

        var authState = await _authStateProvider.GetAuthenticationStateAsync();
        _cachedUser = authState.User;
        return _cachedUser;
    }
}

记得在Program.cs里注册这个服务:

builder.Services.AddScoped<IUserContext, UserContext>();

第二步:让实体实现审计接口

先定义一个审计接口,统一规范需要填充的字段:

public interface IAuditable
{
    string CreatedBy { get; set; }
    string LastModifiedBy { get; set; }
    // 要是需要时间字段的话,也可以加上CreatedDate、LastModifiedDate
}

然后让你的实体都实现这个接口:

public class Product : IAuditable
{
    public int Id { get; set; }
    public string Name { get; set; }
    // 其他业务属性...
    public string CreatedBy { get; set; }
    public string LastModifiedBy { get; set; }
}

第三步:在DbContext中自动填充审计字段

现在把IUserContext注入到DbContext里,重写SaveChangesAsync方法,在保存数据前自动处理审计字段:

public class AppDbContext : DbContext
{
    private readonly IUserContext _userContext;

    public AppDbContext(DbContextOptions<AppDbContext> options, IUserContext userContext)
        : base(options)
    {
        _userContext = userContext;
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        // 获取当前用户的标识(这里用NameIdentifier,你可以换成自己需要的Claim类型)
        var currentUser = await _userContext.GetCurrentUserAsync();
        var userId = currentUser.FindFirstValue(ClaimTypes.NameIdentifier);

        // 处理新增实体:同时设置CreatedBy和LastModifiedBy
        foreach (var entry in ChangeTracker.Entries().Where(e => e.State == EntityState.Added))
        {
            if (entry.Entity is IAuditable auditable)
            {
                auditable.CreatedBy = userId;
                auditable.LastModifiedBy = userId;
            }
        }

        // 处理修改实体:只更新LastModifiedBy,不要覆盖CreatedBy
        foreach (var entry in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified))
        {
            if (entry.Entity is IAuditable auditable)
            {
                auditable.LastModifiedBy = userId;
            }
        }

        return await base.SaveChangesAsync(cancellationToken);
    }
}

为什么这个方案靠谱?

  • IUserContext是Scoped生命周期,和Blazor组件的上下文一致,能保证在调用SaveChangesAsync时,用户已经完成认证,AuthenticationState已经初始化完成,不会出现之前的报错。
  • 把审计逻辑统一放在EF Core层面,符合关注点分离,不用在每个Blazor组件里重复写手动设置字段的代码。
  • 还能灵活处理特殊情况,比如后台任务触发的保存操作,这时候可以在GetCurrentUserAsync里判断是否是匿名用户,设置默认值或者跳过填充。

这样一来,你就不用再手动在Blazor层设置审计字段了,EF Core会自动帮你搞定,省心很多~

备注:内容来源于stack exchange,提问作者Jean Baumflek

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 11:24:38