.NET EF Core 3.1使用NLog :format=@更新大模型遇DB错误时内存泄漏
解决EF Core结合NLog ${exception:format=@} 导致SaveChangesAsync内存耗尽的问题
问题场景
执行以下代码时,一旦触发数据库错误「String or binary data would be truncated」(字符串/二进制数据过长,无法存入对应字段),SaveChangesAsync会直接挂起,内存持续上涨直到耗尽:
context.Update(myBigObject); await context.SaveChangesAsync(); // 此处出现异常后挂起
这个问题只会在NLog配置使用${exception:format=@}时触发——改用${exception:format=toString}能临时解决,但会丢失内部异常的详细信息。另外该问题在Serilog中也会出现,推测根源和EF Core有关。
不用改NLog格式的解决方案
方案1:捕获异常时手动清理EF Core的异常引用
在捕获DbUpdateException时,手动创建一个仅包含关键信息的干净异常对象,避免EF Core内部的循环引用或大对象被NLog深度序列化:
try { await context.SaveChangesAsync(); } catch (DbUpdateException ex) { // 只保留异常消息和内部异常,剥离EF Core上下文引用 var cleanException = new Exception(ex.Message, ex.InnerException); logger.Error(cleanException, "数据保存失败"); // 要继续抛出异常的话,扔原始的就行 throw; }
方案2:给NLog加自定义异常渲染器
针对DbUpdateException做特殊处理,其他异常依然用@格式完整序列化:
- 写一个自定义渲染器类:
public class CleanDbUpdateExceptionRenderer : ExceptionLayoutRenderer { protected override void Append(StringBuilder builder, LogEventInfo logEvent) { if (logEvent.Exception is DbUpdateException dbEx) { // 拼接包含内部异常的完整信息,避免序列化EF内部对象 var logContent = $"[{nameof(DbUpdateException)}] {dbEx.Message}\n"; if (dbEx.InnerException != null) { logContent += $"内部异常: {dbEx.InnerException.Message}\n"; } if (!string.IsNullOrWhiteSpace(dbEx.StackTrace)) { logContent += $"堆栈信息:\n{dbEx.StackTrace}"; } builder.Append(logContent); } else { // 非DbUpdateException用默认的@格式 base.Append(builder, logEvent); } } }
- 在NLog配置里注册并使用这个渲染器:
<nlog> <extensions> <add assembly="你的程序集名称"/> </extensions> <targets> <target name="fileLog" type="File" fileName="logs/${shortdate}.log"> <layout>${longdate} ${level} ${message} ${cleanDbUpdateException:format=@}</layout> </target> </targets> <rules> <logger name="*" minlevel="Error" writeTo="fileLog"/> </rules> </nlog>
方案3:限制EF Core的敏感日志输出
在DbContext配置里禁用敏感数据日志,减少EF Core异常中携带的数据量,从根源降低序列化负担:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseSqlServer("你的连接字符串") .EnableSensitiveDataLogging(false); // 关闭敏感数据日志,减少异常里的冗余数据 }
内容的提问来源于stack exchange,提问作者Mintesnot
相关产品推荐
相关产品推荐

