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




