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

使用动态代理实体类型时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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:25:16