Joã*_*iva 4 asp.net-identity .net-core asp.net-core asp.net-core-2.0
我已经使用 Visual Studio 中的模板和 Identity 预设(存储在应用程序中的用户帐户)创建了一个 .net core 2.1 MVC 应用程序,并且我正在尝试自动化一些审计字段。
基本上我想要做的是覆盖 SaveChangesAsync() 方法,以便每当对实体进行更改时,当前登录的用户 ID 都会设置为 CreatedBy 或 ModifiedBy 属性的审计属性,这些属性被创建为实体的影子属性.
我看过似乎有很多答案,但令人惊讶的是,没有一个对我有用。我曾尝试注入 IHttpContext、HttpContext、UserManager,但我似乎无法访问返回用户 ID 的方法,或者出现循环依赖错误,我不太明白为什么会发生这种情况。
我真的对这个感到绝望。我认为这样的事情应该很简单,但我真的很难弄清楚如何去做。似乎有针对 web api 控制器或 MVC 控制器的有据可查的解决方案,但不能在 ApplicationDbContext 中使用。
如果有人可以帮助我或至少为我指明正确的方向,我将不胜感激,谢谢。
让我们称之为 DbContextWithUserAuditing
public class DBContextWithUserAuditing : IdentityDbContext<ApplicationUser, ApplicationRole, string>
{
public string UserId { get; set; }
public int? TenantId { get; set; }
public DBContextWithUserAuditing(DbContextOptions<DBContextWithUserAuditing> options) : base(options) { }
// here we declare our db sets
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.NamesToSnakeCase(); // PostgreSQL
modelBuilder.EnableSoftDelete();
}
public override int SaveChanges()
{
ChangeTracker.DetectChanges();
ChangeTracker.ProcessModification(UserId);
ChangeTracker.ProcessDeletion(UserId);
ChangeTracker.ProcessCreation(UserId, TenantId);
return base.SaveChanges();
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
ChangeTracker.DetectChanges();
ChangeTracker.ProcessModification(UserId);
ChangeTracker.ProcessDeletion(UserId);
ChangeTracker.ProcessCreation(UserId, TenantId);
return (await base.SaveChangesAsync(true, cancellationToken));
}
}
Run Code Online (Sandbox Code Playgroud)
然后你有请求管道和你需要的 - 是一个过滤器钩子,你可以在其中设置你的用户 ID
public class AppInitializerFilter : IAsyncActionFilter
{
private DBContextWithUserAuditing _dbContext;
public AppInitializerFilter(
DBContextWithUserAuditing dbContext
)
{
_dbContext = dbContext;
}
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next
)
{
string userId = null;
int? tenantId = null;
var claimsIdentity = (ClaimsIdentity)context.HttpContext.User.Identity;
var userIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
if (userIdClaim != null)
{
userId = userIdClaim.Value;
}
var tenantIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == CustomClaims.TenantId);
if (tenantIdClaim != null)
{
tenantId = !string.IsNullOrEmpty(tenantIdClaim.Value) ? int.Parse(tenantIdClaim.Value) : (int?)null;
}
_dbContext.UserId = userId;
_dbContext.TenantId = tenantId;
var resultContext = await next();
}
}
Run Code Online (Sandbox Code Playgroud)
您可以通过以下方式激活此过滤器(Startup.cs 文件)
services
.AddMvc(options =>
{
options.Filters.Add(typeof(OnRequestInit));
})
Run Code Online (Sandbox Code Playgroud)
然后,您的应用程序能够自动将 UserID 和 TenantID 设置为新创建的记录
public static class ChangeTrackerExtensions
{
public static void ProcessCreation(this ChangeTracker changeTracker, string userId, int? tenantId)
{
foreach (var item in changeTracker.Entries<IHasCreationTime>().Where(e => e.State == EntityState.Added))
{
item.Entity.CreationTime = DateTime.Now;
}
foreach (var item in changeTracker.Entries<IHasCreatorUserId>().Where(e => e.State == EntityState.Added))
{
item.Entity.CreatorUserId = userId;
}
foreach (var item in changeTracker.Entries<IMustHaveTenant>().Where(e => e.State == EntityState.Added))
{
if (tenantId.HasValue)
{
item.Entity.TenantId = tenantId.Value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我不建议将HttpContext,UserManager或任何东西注入你的 DbContext 类,因为这样你就违反了单一职责原则。
感谢所有的答案。最后,我决定创建一个 UserResolveService,它通过 DI 接收 HttpContextAccessor,然后获取当前用户的名称。通过该名称,我可以查询数据库以获取我可能需要的任何信息。然后我将此服务注入到 ApplicationDbContext 上。
IUserResolveService.cs
public interface IUserResolveService
{
Task<string> GetCurrentSessionUserId(IdentityDbContext dbContext);
}
Run Code Online (Sandbox Code Playgroud)
UserResolveService.cs
public class UserResolveService : IUserResolveService
{
private readonly IHttpContextAccessor httpContextAccessor;
public UserResolveService(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public async Task<string> GetCurrentSessionUserId(IdentityDbContext dbContext)
{
var currentSessionUserEmail = httpContextAccessor.HttpContext.User.Identity.Name;
var user = await dbContext.Users
.SingleAsync(u => u.Email.Equals(currentSessionUserEmail));
return user.Id;
}
}
Run Code Online (Sandbox Code Playgroud)
您必须在启动时注册该服务并将其注入到 ApplicationDbContext 中,然后您可以像这样使用它:
应用程序DbContext.cs
var dbContext = this;
var currentSessionUserId = await userResolveService.GetCurrentSessionUserId(dbContext);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2468 次 |
| 最近记录: |