【问题标题】:Get current user ID in Entity Framework Core without having tight coupling在 Entity Framework Core 中获取当前用户 ID,无需紧密耦合
【发布时间】:2026-01-28 12:15:03
【问题描述】:

我有一个 REST API 服务,它需要验证任何类型的用户(例如 Windows 用户)并将对它的引用添加到数据库表中以供以后使用。

为此,我需要知道 Entity Framework Core 中的当前用户,因为用户被存储为实体的创建者。

我正在尝试从 DbContext 中抽象出当前用户在 SaveChanges 方法中的耦合,其中在创建实体时用户正在耦合。

但是如何获取DbContext中的当前用户呢?我正在尝试使用委托,但我陷入了 DI 需要注册 UserService 的方式,该 UserService 抽象了用户并返回了 ID。

【问题讨论】:

  • 通常应用程序不会在数据库中创建单个用户,而是为服务/应用程序使用共享连接字符串,在这种情况下,数据库的用户不是你想要的,而是你想要的用户应用程序。所以你不再处于 ef 核心效果区,那么你有没有在数据库中单独创建用户?
  • @T.Nielsen 我不想拥有数据库的用户,而是外部用户,这就是为什么我需要某种委托才能在 DbContext 中获取它,因为 DbContext 没有需要知道用户来自哪里。
  • 一些代码可能有助于阐明您想要做什么。我所了解的是,您希望将用户与 DbContext 的未知来源(连接字符串?)分离,但您不能,因为您的 UserService 以某种无济于事的方式注册,并且您在创建时需要用户 ID DbContext...我不明白。

标签: c# design-patterns architecture entity-framework-core


【解决方案1】:

我认为我们可能需要更多细节,但至少如果你在一个 .net web api controllerbase 上下文中你有什么

System.Security.Principal.IIdentity user = User?.Identity;
if (user != null && user.IsAuthenticated)
    System.Diagnostics.Debug.WriteLine($"Username: {user.Name}");

从这个意义上说,这在很大程度上取决于将哪些声明放在您可以找到什么的地方,如果您使用完全不同的身份验证方案,情况可能会有所不同:)

【讨论】:

    【解决方案2】:

    如果我有一个对象

    public myObj
    {
       public int myObjID {get; set;}
       public string ObjectDetails {get; set;}
    
    # I would add this field
       public string UserName {get; set;}
    
    }
    

    然后在我将存储对象的控制器操作中,我会这样做:

    myObj.UserName = User.Identity?.Name ?? "";
    

    如果[Authorize] 已应用于控制器,则空值检查尤其多余,因为您无法在未经授权的情况下执行操作,并且应用程序要求您拥有用户名(至少)。

    如果您需要找出谁做了什么:

    var obj = await db.myObj.where(x=> x.UserName == User.Identity.Name).ToListAsync()
    

    
    var obj = await db.myObj.where(x=> x.UserName == uname).ToListAsync();
    

    其中uname 是方法参数等。

    【讨论】:

      【解决方案3】:

      我之前考虑过这个问题,现在我想出了以下解决方案:

      我已经制作了接受多种策略的处理器(在我的情况下,对于每个 CRUD 操作,我都制定了一种策略来执行)。当 EF 进行更改时调用处理器(通过SaveChangesAsyncSaveChanges

      首先我的SaveChanges(Asyc) 覆盖:

           public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
              {
                  foreach (var entry in ChangeTracker.Entries())
                  {
                      await _dataOperationProcessor.ProcessAsync(entry, cancellationToken);
                  }
      
                  return await base.SaveChangesAsync(cancellationToken);
              }
      

      处理器:

       public class DataOperationProcessor : IDataOperationProcessor
          {
              private readonly IEnumerable<IEntityActionStrategy> _entityActionStrategies;   
      
              public DataOperationProcessor(IEnumerable<IEntityActionStrategy> entityActionStrategies)
              {
                  _entityActionStrategies = entityActionStrategies;            
              }
      
              public void Process(EntityEntry entry)
              {
                  foreach (var stategy in _entityActionStrategies.Where(e => e.ForEntityState == entry.State))
                  {
                      stategy.Execute(entry);
                  }
              }
      
              public async Task ProcessAsync(EntityEntry entry, CancellationToken cancellationToken)
              {
                  foreach (var stategy in _entityActionStrategies.Where(e => e.ForEntityState == entry.State))
                  {
                      await stategy.ExecuteAsync(entry, cancellationToken);
                  }
              }
          }
      

      添加实体时调用的策略:

      public class AuditableEntityCreatedStrategy : BaseEntityActionStrategy<BaseAuditableEntity>
          {
              private readonly IUserAccountInfoResolver _userAccountInfoResolver;
      
              public AuditableEntityCreatedStrategy(IUserAccountInfoResolver userAccountInfoResolver)
              {
                  _userAccountInfoResolver = userAccountInfoResolver;
              }
      
              public override EntityState ForEntityState => EntityState.Added;
      
              protected override void ExecuteForType(EntityEntry entry, BaseAuditableEntity entity)
              {
                  var userId = _userAccountInfoResolver.ResolveCurrentUser().UserId;         
                  var dbUser = (entry.Context as SecurityContext).Users.Single(u => u.UserId == userId);
      
                  entry.CurrentValues[nameof(BaseAuditableEntity.CreatedById)] = dbUser.Id;
                  entry.CurrentValues[nameof(BaseAuditableEntity.Created)] = DateTime.Now;
              }
          }    
      
      
      
      

      【讨论】: