You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让DataContext同时集成Duende Identity Server与数据库审计功能?

解决方案

思路说明

C#不支持类的多重继承,因此无法让DataContext同时继承ApiAuthorizationDbContext<ApplicationUser>和AuditableIdentityContext。我们可以通过迁移审计逻辑、EF Core拦截器或调整继承链三种方式,将审计功能整合到基于ApiAuthorizationDbContext的上下文之中,同时保留Duende Identity Server的相关功能。

方案1:直接迁移审计逻辑到DataContext

如果你的AuditableIdentityContext核心逻辑是在SaveChanges/SaveChangesAsync中处理审计字段(如创建人、创建时间、更新人、更新时间),可以直接将这些逻辑搬到继承ApiAuthorizationDbContext的DataContext里:

  1. 定义审计字段接口(若之前未定义):
public interface IAuditableEntity
{
    string CreatedBy { get; set; }
    DateTime CreatedDate { get; set; }
    string UpdatedBy { get; set; }
    DateTime UpdatedDate { get; set; }
}
  1. 让业务实体实现该接口:
public class Product : IAuditableEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    // 审计字段
    public string CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public string UpdatedBy { get; set; }
    public DateTime UpdatedDate { get; set; }
}
  1. 修改DataContext,重写保存方法并注入审计逻辑:
public class DataContext : ApiAuthorizationDbContext<ApplicationUser>
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public DataContext(DbContextOptions<DataContext> options, IHttpContextAccessor httpContextAccessor) 
        : base(options)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    // 自定义DbSet
    public DbSet<Product> Products { get; set; }

    public override int SaveChanges(bool acceptAllChangesOnSuccess)
    {
        ApplyAuditRules();
        return base.SaveChanges(acceptAllChangesOnSuccess);
    }

    public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
    {
        ApplyAuditRules();
        return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
    }

    private void ApplyAuditRules()
    {
        var currentUser = _httpContextAccessor.HttpContext?.User?.Identity?.Name ?? "System";
        var now = DateTime.UtcNow;

        foreach (var entry in ChangeTracker.Entries<IAuditableEntity>())
        {
            switch (entry.State)
            {
                case EntityState.Added:
                    entry.Entity.CreatedBy = currentUser;
                    entry.Entity.CreatedDate = now;
                    entry.Entity.UpdatedBy = currentUser;
                    entry.Entity.UpdatedDate = now;
                    break;
                case EntityState.Modified:
                    entry.Entity.UpdatedBy = currentUser;
                    entry.Entity.UpdatedDate = now;
                    // 锁定创建字段,防止被修改
                    entry.Property(x => x.CreatedBy).IsModified = false;
                    entry.Property(x => x.CreatedDate).IsModified = false;
                    break;
            }
        }

        // 若需记录审计日志到数据库,可在此生成AuditLog实体并添加到DbSet
        // var logs = GenerateAuditLogs();
        // AuditLogs.AddRange(logs);
    }
}

方案2:用EF Core拦截器实现解耦审计

如果审计逻辑原本是通过拦截器实现的,可直接将拦截器注册到DataContext中,无需修改上下文继承关系:

  1. 创建审计拦截器:
public class AuditInterceptor : SaveChangesInterceptor
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AuditInterceptor(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
    {
        ApplyAuditRules(eventData.Context);
        return base.SavingChanges(eventData, result);
    }

    public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
    {
        ApplyAuditRules(eventData.Context);
        return base.SavingChangesAsync(eventData, result, cancellationToken);
    }

    private void ApplyAuditRules(DbContext context)
    {
        if (context == null) return;

        var currentUser = _httpContextAccessor.HttpContext?.User?.Identity?.Name ?? "System";
        var now = DateTime.UtcNow;

        foreach (var entry in context.ChangeTracker.Entries<IAuditableEntity>())
        {
            switch (entry.State)
            {
                case EntityState.Added:
                    entry.Entity.CreatedBy = currentUser;
                    entry.Entity.CreatedDate = now;
                    entry.Entity.UpdatedBy = currentUser;
                    entry.Entity.UpdatedDate = now;
                    break;
                case EntityState.Modified:
                    entry.Entity.UpdatedBy = currentUser;
                    entry.Entity.UpdatedDate = now;
                    entry.Property(x => x.CreatedBy).IsModified = false;
                    entry.Property(x => x.CreatedDate).IsModified = false;
                    break;
            }
        }
    }
}
  1. 在服务注册时添加拦截器:
// 注册HttpContextAccessor用于获取当前用户
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<AuditInterceptor>();

// 配置DataContext并注入拦截器
builder.Services.AddDbContext<DataContext>((sp, options) =>
{
    var interceptor = sp.GetRequiredService<AuditInterceptor>();
    options.UseSqlServer("Your_Connection_String")
           .AddInterceptors(interceptor);
});

// 保留Duende Identity Server配置
builder.Services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, DataContext>();

方案3:调整AuditableIdentityContext的继承链(自定义场景)

如果AuditableIdentityContext是你自行实现的,可以修改它的继承关系,让它继承ApiAuthorizationDbContext<ApplicationUser>,再让DataContext继承这个修改后的审计上下文:

// 修改AuditableIdentityContext的基类
public class AuditableIdentityContext : ApiAuthorizationDbContext<ApplicationUser>
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AuditableIdentityContext(DbContextOptions<AuditableIdentityContext> options, IHttpContextAccessor httpContextAccessor)
        : base(options)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    // 原审计逻辑,重写保存方法
    public override int SaveChanges(bool acceptAllChangesOnSuccess)
    {
        ApplyAuditRules();
        return base.SaveChanges(acceptAllChangesOnSuccess);
    }

    private void ApplyAuditRules()
    {
        // 审计逻辑实现...
    }
}

// DataContext继承修改后的审计上下文
public class DataContext : AuditableIdentityContext
{
    public DataContext(DbContextOptions<DataContext> options, IHttpContextAccessor httpContextAccessor)
        : base(options, httpContextAccessor)
    {
    }

    // 自定义DbSet...
}

注意事项

  • 确保注册IHttpContextAccessor服务,否则无法获取当前登录用户信息。
  • 若审计逻辑包含审计日志表,需在DataContext中添加对应DbSet,并通过EF迁移更新数据库结构。
  • 整合后需测试Identity Server核心功能(令牌生成、用户管理)与审计功能(字段自动填充、日志记录)是否正常运行。

内容的提问来源于stack exchange,提问作者Tim Cadieux

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 14:15:34