基于Azure AD的ASP.NET实体Author与Editor字段追踪问题
在Azure AD环境下为ASP.NET实体追踪Created By/Modified By字段
我来给你梳理下这个场景下的解决方案,毕竟没有ApplicationUser实体可用,得换个思路从Azure AD的身份信息入手,同时用EF Core的拦截器来自动处理字段填充,不用重复写代码。
第一步:定义可审计的实体基类
先给所有需要追踪的实体建个基类,把通用的审计字段都放进去,这样每个实体直接继承就行:
public abstract class AuditableEntity { // 可以根据需求选存用户ObjectId(更稳定)或者用户名 public string CreatedBy { get; set; } public DateTime CreatedDate { get; set; } public string ModifiedBy { get; set; } public DateTime ModifiedDate { get; set; } }
比如你的Product实体就可以这样写:
public class Product : AuditableEntity { public int Id { get; set; } public string Name { get; set; } // 其他业务字段... }
第二步:获取当前Azure AD用户的信息
因为用Azure AD认证,当前用户的身份信息存在HttpContext的Claims里,我们可以封装一个服务来统一获取,避免到处写重复的Claims查询逻辑:
public interface ICurrentUserService { // 获取Azure AD用户的唯一标识(ObjectId) string GetCurrentUserObjectId(); // 获取用户的显示名称或邮箱 string GetCurrentUserDisplayName(); } public class CurrentUserService : ICurrentUserService { private readonly IHttpContextAccessor _httpContextAccessor; public CurrentUserService(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public string GetCurrentUserObjectId() { return _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier); } public string GetCurrentUserDisplayName() { // 优先取PreferredUsername(通常是邮箱),没有的话取Identity.Name return _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.PreferredUsername) ?? _httpContextAccessor.HttpContext?.User?.Identity?.Name ?? "System"; // 后台任务等无上下文场景用"System"标识 } }
然后在Program.cs里注册这个服务:
builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped<ICurrentUserService, CurrentUserService>();
第三步:用EF Core拦截器自动填充审计字段
这一步是核心,通过EF Core的SaveChangesInterceptor,在保存实体前自动处理CreatedBy/ModifiedBy等字段,不用在每个业务逻辑里手动设置:
public class AuditableEntitySaveChangesInterceptor : SaveChangesInterceptor { private readonly ICurrentUserService _currentUserService; private readonly TimeProvider _timeProvider; public AuditableEntitySaveChangesInterceptor(ICurrentUserService currentUserService, TimeProvider timeProvider) { _currentUserService = currentUserService; _timeProvider = timeProvider; } // 同步保存时的处理 public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result) { UpdateAuditableEntities(eventData.Context); return base.SavingChanges(eventData, result); } // 异步保存时的处理 public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default) { UpdateAuditableEntities(eventData.Context); return base.SavingChangesAsync(eventData, result, cancellationToken); } private void UpdateAuditableEntities(DbContext? context) { if (context == null) return; var currentUser = _currentUserService.GetCurrentUserDisplayName(); // 或者用GetCurrentUserObjectId() var currentUtcTime = _timeProvider.GetUtcNow().DateTime; // 遍历所有继承AuditableEntity的实体 foreach (var entry in context.ChangeTracker.Entries<AuditableEntity>()) { switch (entry.State) { case EntityState.Added: // 新增时同时设置创建和修改字段 entry.Entity.CreatedBy = currentUser; entry.Entity.CreatedDate = currentUtcTime; entry.Entity.ModifiedBy = currentUser; entry.Entity.ModifiedDate = currentUtcTime; break; case EntityState.Modified: // 修改时只更新修改字段,禁止修改创建字段 entry.Entity.ModifiedBy = currentUser; entry.Entity.ModifiedDate = currentUtcTime; entry.Property(x => x.CreatedBy).IsModified = false; entry.Property(x => x.CreatedDate).IsModified = false; break; } } } }
然后把这个拦截器注册到你的DbContext:
builder.Services.AddDbContext<YourDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")) .AddInterceptors(sp => sp.GetRequiredService<AuditableEntitySaveChangesInterceptor>())); builder.Services.AddScoped<AuditableEntitySaveChangesInterceptor>();
一些额外的注意事项
- 如果你需要存储用户的唯一标识(比如后续要关联Azure AD用户信息),推荐用
GetCurrentUserObjectId()返回的ObjectId,比用户名更稳定 - 后台任务或者没有HttpContext的场景,
CurrentUserService会返回"System",你可以根据需求调整这个默认值 - 要确保所有需要追踪的实体都继承
AuditableEntity基类,否则拦截器不会处理
内容的提问来源于stack exchange,提问作者S. Walker
相关产品推荐
相关产品推荐

