You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ASP.NET MVC5中Audit.Net与Audit.EntityFramework性能问题咨询

解决Audit.Net + EntityFramework批量角色分配的性能问题

我之前碰到过类似的场景,核心问题其实是循环调用AddToRole时,每一次操作都会触发独立的SaveChanges,而Audit.EntityFramework默认会为每一次SaveChanges生成完整的审计日志——700次操作就意味着700条审计记录的生成与存储,这自然会带来明显的性能开销。下面是几个针对性的优化方案:

1. 直接使用批量角色分配方法

ASP.NET Identity的UserManager本身就提供了批量添加角色的方法AddToRoles(复数形式),它内部会批量处理所有角色分配请求,只触发一次SaveChanges

// 直接传入所有角色名称的数组,替代循环调用AddToRole
base.UserManager.AddToRoles(user.Id, roles.Select(r => r.Name).ToArray());

这个方法既简洁又高效,Audit.Net只会为这一次SaveChanges生成单条审计记录,能直接解决你遇到的性能问题。

2. 手动合并DbContext操作,减少SaveChanges次数

如果需要更灵活的控制,可以绕过UserManager的封装,直接操作DbContext批量添加角色关联,只执行一次SaveChanges

// 假设你的DbContext是ApplicationDbContext
using (var context = new ApplicationDbContext())
{
    var user = context.Users.Find(user.Id);
    foreach (var role in roles)
    {
        // 直接操作UserRoles关联表,避免每次调用AddToRole触发SaveChanges
        context.UserRoles.Add(new IdentityUserRole
        {
            UserId = user.Id,
            RoleId = role.Id // 建议用RoleId而非Name,更高效且避免名称重复问题
        });
    }
    // 仅执行一次SaveChanges,对应单条审计日志
    context.SaveChanges();
}

3. 调整Audit.Net配置,优化审计行为

如果因为业务限制必须保留循环调用AddToRole的逻辑,可以通过Audit.Net的配置来合并审计记录:

3.1 用审计Scope包裹批量操作

关闭自动审计,手动创建一个审计Scope包裹整个循环,让所有操作合并到同一条审计记录中:

// 在Startup.cs中设置Audit.Net为OptOut模式(默认不审计)
Audit.EntityFramework.Configuration.Setup()
    .ForContext<ApplicationDbContext>(config => config
        .IncludeEntityObjects()
        .AuditEventType("EF:{context}"))
    .UseOptOut();

// 在Action中手动审计批量操作
using (var auditScope = AuditScope.Create("批量角色分配", () => new { UserId = user.Id }))
{
    foreach (var role in roles)
    {
        base.UserManager.AddToRole(user.Id, role.Name);
    }
    // 添加自定义字段记录分配的角色列表
    auditScope.SetCustomField("AssignedRoles", roles.Select(r => r.Name));
}

3.2 过滤不必要的审计实体

如果不需要审计IdentityUserRole关联表的变化,可以在配置中忽略它,减少审计数据的生成:

Audit.EntityFramework.Configuration.Setup()
    .ForContext<ApplicationDbContext>(config => config
        .IncludeEntityObjects()
        .AuditEventType("EF:{context}")
        .IgnoreEntities<IdentityUserRole>()) // 忽略UserRoles表的审计

注:此方法需根据你的审计需求判断,如果必须记录角色分配细节则不适用。

4. 优化审计日志存储

如果审计日志的存储是性能瓶颈,可以尝试:

  • 异步存储审计日志:使用Audit.Net的异步数据提供者,让审计存储不阻塞主业务流程:
Audit.Core.Configuration.Setup()
    .UseAsyncProvider(new SqlDataProvider()
    {
        ConnectionString = "你的数据库连接字符串",
        TableName = "AuditLogs"
    });
  • 批量插入审计记录:自定义数据提供者,将多条审计记录批量插入数据库,减少数据库IO开销。

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

火山引擎 最新活动