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

.NET Core跨DbContext设置CreatedBy默认值的实现咨询

解决方案

要在ManagementContext中设置CreatedBy字段的值,核心思路是通过依赖注入获取当前用户的身份信息,而不是直接访问另一个DbContext(这会导致不必要的耦合)。下面是具体的实现步骤:

1. 创建一个获取当前用户ID的服务

首先定义一个接口来抽象获取当前用户的逻辑,这样可以避免直接依赖HttpContext,也方便后续测试:

public interface ICurrentUserService
{
    string? GetCurrentUserId();
}

然后实现这个接口,利用IHttpContextAccessor从请求的Claims中获取用户ID(假设你的用户ID存储在NameIdentifier Claim中):

public class CurrentUserService : ICurrentUserService
{
    private readonly IHttpContextAccessor _httpContextAccessor;

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

    public string? GetCurrentUserId()
    {
        return _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
    }
}

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

builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ICurrentUserService, CurrentUserService>();

2. 在ManagementContext中注入ICurrentUserService

修改ManagementContext的构造函数,注入ICurrentUserService,这样就能在保存变更时获取用户ID:

namespace Infrastructure.Data 
{ 
    public class ManagementContext : DbContext 
    { 
        private readonly ICurrentUserService _currentUserService;

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

        public DbSet<Department> Department { get; set; } 

        public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) 
        { 
            var addedEntities = ChangeTracker.Entries()
                .Where(e => e.State == EntityState.Added)
                .ToList(); 

            addedEntities.ForEach(e => 
            { 
                e.Property("CreatedOn").CurrentValue = DateTime.UtcNow; 
                // 设置CreatedBy字段
                var userId = _currentUserService.GetCurrentUserId();
                if (!string.IsNullOrEmpty(userId))
                {
                    e.Property("CreatedBy").CurrentValue = userId;
                }
                // 可选:如果没有用户ID(比如后台任务),可以抛出异常或设置默认值
                // else throw new InvalidOperationException("无法获取当前用户ID");
            }); 

            var editedEntities = ChangeTracker.Entries()
                .Where(e => e.State == EntityState.Modified)
                .ToList(); 

            editedEntities.ForEach(e => 
            { 
                e.Property("LastModifiedOn").CurrentValue = DateTime.UtcNow; 
                // 可选:也可以设置LastModifiedBy字段
                // var userId = _currentUserService.GetCurrentUserId();
                // if (!string.IsNullOrEmpty(userId))
                // {
                //     e.Property("LastModifiedBy").CurrentValue = userId;
                // }
            }); 

            return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); 
        } 
    } 
}

3. 处理无HttpContext的场景(可选)

如果你的应用有后台任务、定时任务等没有HttpContext的场景,你可以扩展ICurrentUserService,允许手动设置用户ID:

public interface ICurrentUserService
{
    string? GetCurrentUserId();
    void SetCurrentUserId(string userId);
}

public class CurrentUserService : ICurrentUserService
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private string? _manualUserId;

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

    public string? GetCurrentUserId()
    {
        // 优先使用手动设置的ID,再从HttpContext获取
        return _manualUserId ?? _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
    }

    public void SetCurrentUserId(string userId)
    {
        _manualUserId = userId;
    }
}

然后在后台任务中,就可以手动设置用户ID:

using (var scope = _serviceProvider.CreateScope())
{
    var currentUserService = scope.ServiceProvider.GetRequiredService<ICurrentUserService>();
    var managementContext = scope.ServiceProvider.GetRequiredService<ManagementContext>();

    // 设置后台任务执行的用户ID
    currentUserService.SetCurrentUserId("system-task-user-id");

    // 执行数据操作
    managementContext.Department.Add(new Department { Name = "Test Dept" });
    await managementContext.SaveChangesAsync();
}

为什么不直接访问另一个DbContext?

直接在ManagementContext中注入另一个DbContext会导致两个上下文之间产生强耦合,违反了单一职责原则。而且如果两个上下文使用不同的数据库连接,还可能引发事务、性能等问题。通过ICurrentUserService抽象用户信息的获取,既解耦了依赖,也让代码更易于测试和维护。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:50:43