使用动态代理实体类型时Audit.Net出现无响应问题求助
解决Audit.Net处理EF动态代理对象时无响应的问题
我之前也碰到过类似的坑——EF生成的动态代理对象因为包含额外的追踪引用和循环依赖,会导致Audit.Net在序列化对象时陷入无限循环,最终卡死在AuditScope.Create()这一步。下面是几个经过验证的解决方案,你可以根据自己的场景选择:
方案1:提取干净的实体对象(推荐)
直接从代理对象中复制属性到一个新建的FileRecord实例(非代理类型),让Audit.Net追踪这个干净的对象。这样既保留了审计变更的能力,又避免了代理的序列化问题:
var record = _context.FileRecord.Include(a => a.Contact).SingleOrDefault(c => c.Id == id); // 手动复制属性到干净的实体实例 var auditTarget = new FileRecord { Id = record.Id, ContactId = record.ContactId, // 复制其他需要审计的字段 Contact = new Contact { Id = record.Contact.Id, Name = record.Contact.Name // 只复制需要审计的关联属性 } }; using(var audit = AuditScope.Create(new AuditScopeOptions { EventType = "Update", TargetGetter = () => auditTarget })) { // 更新实际的EF实体 record.ContactId = 33; // 同步更新审计目标对象,确保能追踪到变更 auditTarget.ContactId = 33; } _context.SaveChanges();
如果实体属性较多,用AutoMapper会更高效:
// 提前配置AutoMapper(可以放在启动代码里) Mapper.Initialize(cfg => { cfg.CreateMap(record.GetType(), typeof(FileRecord)); // 代理类型到原始实体的映射 cfg.CreateMap(record.Contact.GetType(), typeof(Contact)); }); var record = _context.FileRecord.Include(a => a.Contact).SingleOrDefault(c => c.Id == id); var auditTarget = Mapper.Map<FileRecord>(record); using(var audit = AuditScope.Create(new AuditScopeOptions { EventType = "Update", TargetGetter = () => auditTarget })) { record.ContactId = 33; auditTarget.ContactId = 33; } _context.SaveChanges();
方案2:临时禁用EF动态代理
在查询需要审计的实体时,临时关闭DbContext的代理创建功能,这样返回的就是原始实体类型,不会触发序列化问题:
// 临时禁用代理创建 _context.Configuration.ProxyCreationEnabled = false; var record = _context.FileRecord.Include(a => a.Contact).SingleOrDefault(c => c.Id == id); // 用完恢复,避免影响其他逻辑 _context.Configuration.ProxyCreationEnabled = true; using(var audit = AuditScope.Create(new AuditScopeOptions { EventType = "Update", TargetGetter = () => record })) { record.ContactId = 33; } _context.SaveChanges();
⚠️ 注意:这个方案会失去EF的延迟加载功能,所以只适合在明确不需要延迟加载的查询场景中使用。
方案3:配置Audit.Net的序列化规则
通过自定义Json序列化设置,忽略代理对象的循环引用和额外属性,让Audit.Net能正常序列化代理对象:
using(var audit = AuditScope.Create(new AuditScopeOptions { EventType = "Update", TargetGetter = () => record, SerializerSettings = new Newtonsoft.Json.JsonSerializerSettings { // 忽略循环引用(避免DbContext和实体之间的循环) ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore, // 不保存类型信息(避免序列化代理类型的额外元数据) TypeNameHandling = Newtonsoft.Json.TypeNameHandling.None, // 可选:忽略EF代理生成的私有字段 ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() } })) { record.ContactId = 33; } _context.SaveChanges();
这个方案不需要修改实体获取逻辑,但需要测试序列化后的日志是否包含了你需要的所有字段,避免因为忽略某些属性导致审计信息不全。
问题根源总结
EF动态代理对象会添加诸如__interceptors、DbContext引用等额外成员,Audit.Net默认的序列化逻辑在处理这些对象时,容易触发循环引用和无限递归,最终导致程序无响应而不抛出异常——这也是为什么用普通DTO或新建实体实例时一切正常的原因。
内容的提问来源于stack exchange,提问作者Ottarl
相关产品推荐
相关产品推荐

