如何使用 Net Core 在 DbContext 中获取用户信息
Posted
技术标签:
【中文标题】如何使用 Net Core 在 DbContext 中获取用户信息【英文标题】:How to get user information in DbContext using Net Core 【发布时间】:2016-07-23 21:34:00 【问题描述】:我正在尝试开发一个类库,我想在其中实现自定义DbContext
。在DbContext
的SaveChanges
方法中,我需要获取当前用户的信息(部门、用户名等)以进行审计。 DbContext
代码的一部分如下:
public override int SaveChanges()
// find all changed entities which is ICreateAuditedEntity
var addedAuditedEntities = ChangeTracker.Entries<ICreateAuditedEntity>()
.Where(p => p.State == EntityState.Added)
.Select(p => p.Entity);
var now = DateTime.Now;
foreach (var added in addedAuditedEntities)
added.CreatedAt = now;
added.CreatedBy = ?;
added.CreatedByDepartment = ?
return base.SaveChanges();
想到两个选项:
使用HttpContext.Items
保存用户信息,注入IHttpContextAccessor并从
HttpContext.Items
(在这种情况下DbContext
取决于HttpContext
,是吗
对吗?)
使用 ThreadStatic 对象而不是 HttpContext.Items
并从对象中获取信息(我阅读了一些帖子
ThreadStatic 不安全)
问题:哪个最适合我的情况?您还有其他建议吗?
【问题讨论】:
与其直接在你的 DbContext 中依赖 IHttpContextAccessor 为什么不创建一个像 AuditLogger 这样的服务类并让你的 DbContext 依赖它,AuditLogger 可以依赖 IHttpContextAccessor 作为它自己的内部实现细节 这似乎是一个很好的方法,我会尝试实现它。谢谢。 【参考方案1】:我实现了一种类似于this blog post 中介绍的方法,基本上涉及创建一个服务,该服务将使用依赖注入将HttpContext
(和底层用户信息)注入特定上下文,或者您更喜欢使用它。
一个非常基本的实现可能如下所示:
public class UserResolverService
private readonly IHttpContextAccessor _context;
public UserResolverService(IHttpContextAccessor context)
_context = context;
public string GetUser()
return _context.HttpContext.User?.Identity?.Name;
您只需要在Startup.cs
文件中的ConfigureServices
方法中将其注入管道:
services.AddTransient<UserResolverService>();
最后,只需在您指定的 DbContext
的构造函数中访问它:
public partial class ExampleContext : IExampleContext
private YourContext _context;
private string _user;
public ExampleContext(YourContext context, UserResolverService userService)
_context = context;
_user = userService.GetUser();
然后您应该能够使用_user
在您的上下文中引用当前用户。这可以很容易地扩展到存储/访问当前请求中可用的任何内容。
【讨论】:
return后去掉await关键字,像这样public string GetUser() return _context.HttpContext.User?.Identity?.Name;
现在 IHttpContextAccessor 默认没有在服务中注册。我们必须在 Startup.cs services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
中手动连接它
非常优雅的解决方案,谢谢。我正在考虑添加一个由 UserResolverService 实现的接口,并使 DbContext 使用它而不是 UserResloverService 本身。因此,它会将 deDbContext 与 IHttpContextAccessor 解耦,以帮助单元测试和其他用途。
如果使用 .NET core 2.1,他们添加了 services.AddHttpContextAccessor()
,如果您查看 source code 的底层,它使用 services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>()
服务引用 data\dbcontext 项目。要将服务注入数据项目,您必须引用它。恐怕会造成循环引用【参考方案2】:
感谢@RionWilliams 的原始答案。这就是我们在.Net Core
3.1 中通过DbContext
、AD B2C
用户和Web Api 解决CreatedBy 和UpdatedBy 的方法。 SysStartTime
和 SysEndTime
基本上是 CreatedDate
和 UpdatedDate
,但通过临时表具有版本历史记录(有关任何时间点存储在表中的数据的信息)。
更多关于这里:
https://***.com/a/64776658/3850405
通用接口:
public interface IEntity
public DateTime SysStartTime get; set;
public DateTime SysEndTime get; set;
public int CreatedById get; set;
public User CreatedBy get; set;
public int UpdatedById get; set;
public User UpdatedBy get; set;
数据库上下文:
public class ApplicationDbContext : DbContext
public ApplicationDbContext(
DbContextOptions options) : base(options)
public DbSet<User> User get; set;
public string _currentUserExternalId;
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
var user = await User.SingleAsync(x => x.ExternalId == _currentUserExternalId);
AddCreatedByOrUpdatedBy(user);
return (await base.SaveChangesAsync(true, cancellationToken));
public override int SaveChanges()
var user = User.Single(x => x.ExternalId == _currentUserExternalId);
AddCreatedByOrUpdatedBy(user);
return base.SaveChanges();
public void AddCreatedByOrUpdatedBy(User user)
foreach (var changedEntity in ChangeTracker.Entries())
if (changedEntity.Entity is IEntity entity)
switch (changedEntity.State)
case EntityState.Added:
entity.CreatedBy = user;
entity.UpdatedBy = user;
break;
case EntityState.Modified:
Entry(entity).Reference(x => x.CreatedBy).IsModified = false;
entity.UpdatedBy = user;
break;
protected override void OnModelCreating(ModelBuilder modelBuilder)
foreach (var property in modelBuilder.Model.GetEntityTypes()
.SelectMany(t => t.GetProperties())
.Where(p => p.ClrType == typeof(string)))
if (property.GetMaxLength() == null)
property.SetMaxLength(256);
foreach (var property in modelBuilder.Model.GetEntityTypes()
.SelectMany(t => t.GetProperties())
.Where(p => p.ClrType == typeof(DateTime)))
property.SetColumnType("datetime2(0)");
foreach (var et in modelBuilder.Model.GetEntityTypes())
foreach (var prop in et.GetProperties())
if (prop.Name == "SysStartTime" || prop.Name == "SysEndTime")
prop.ValueGenerated = Microsoft.EntityFrameworkCore.Metadata.ValueGenerated.OnAddOrUpdate;
modelBuilder.Entity<Question>()
.HasOne(q => q.UpdatedBy)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
ExtendedApplicationDbContext:
public class ExtendedApplicationDbContext
public ApplicationDbContext _context;
public UserResolverService _userService;
public ExtendedApplicationDbContext(ApplicationDbContext context, UserResolverService userService)
_context = context;
_userService = userService;
_context._currentUserExternalId = _userService.GetNameIdentifier();
用户解析服务:
public class UserResolverService
public readonly IHttpContextAccessor _context;
public UserResolverService(IHttpContextAccessor context)
_context = context;
public string GetGivenName()
return _context.HttpContext.User.FindFirst(ClaimTypes.GivenName).Value;
public string GetSurname()
return _context.HttpContext.User.FindFirst(ClaimTypes.Surname).Value;
public string GetNameIdentifier()
return _context.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
public string GetEmails()
return _context.HttpContext.User.FindFirst("emails").Value;
启动:
public void ConfigureServices(IServiceCollection services)
services.AddHttpContextAccessor();
services.AddTransient<UserResolverService>();
services.AddTransient<ExtendedApplicationDbContext>();
...
然后可以像这样在任何Controller
中使用:
public class QuestionsController : ControllerBase
private readonly ILogger<QuestionsController> _logger;
private readonly ExtendedApplicationDbContext _extendedApplicationDbContext;
public QuestionsController(ILogger<QuestionsController> logger, ExtendedApplicationDbContext extendedApplicationDbContext)
_logger = logger;
_extendedApplicationDbContext = extendedApplicationDbContext;
【讨论】:
以上是关于如何使用 Net Core 在 DbContext 中获取用户信息的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 ASP.NET Core 从 DbContext 中的 JWT 获取用户名?
ASP.NET Core Web API - 如何在中间件管道中隐藏 DbContext 事务?