Appelons cela DbContextWithUserAuditing
public class DBContextWithUserAuditing : IdentityDbContext
{
public string UserId { get; set; }
public int? TenantId { get; set; }
public DBContextWithUserAuditing(DbContextOptions options) : base(options) { }
// ici nous déclarons nos ensembles de base de données
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 SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
ChangeTracker.DetectChanges();
ChangeTracker.ProcessModification(UserId);
ChangeTracker.ProcessDeletion(UserId);
ChangeTracker.ProcessCreation(UserId, TenantId);
return (await base.SaveChangesAsync(true, cancellationToken));
}
}
Ensuite, vous avez le pipeline de requête et ce dont vous avez besoin est un hook de filtre où vous définissez votre ID utilisateur
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();
}
}
Vous activez ce filtre de la manière suivante (fichier Startup.cs)
services
.AddMvc(options =>
{
options.Filters.Add(typeof(OnRequestInit));
})
Votre application est alors capable de définir automatiquement l'ID utilisateur et l'ID du locataire pour les enregistrements nouvellement créés
public static class ChangeTrackerExtensions
{
public static void ProcessCreation(this ChangeTracker changeTracker, string userId, int? tenantId)
{
foreach (var item in changeTracker.Entries().Where(e => e.State == EntityState.Added))
{
item.Entity.CreationTime = DateTime.Now;
}
foreach (var item in changeTracker.Entries().Where(e => e.State == EntityState.Added))
{
item.Entity.CreatorUserId = userId;
}
foreach (var item in changeTracker.Entries().Where(e => e.State == EntityState.Added))
{
if (tenantId.HasValue)
{
item.Entity.TenantId = tenantId.Value;
}
}
}
Je ne recommande pas d'injecter HttpContext
, UserManager
ou tout autre chose dans votre classe DbContext car de cette manière vous violez le Principe de Responsabilité Unique.