.NET 5 EF Core SaveChangesAsync 因错误而挂起
.NET 5 EF Core SaveChangesAsync hangs on errors
尽管这个问题有很多结果none确实给了我一个明确的答案。
每次我尝试通过 AddAsync 和 SaveChangesAsync 方法插入错误数据(例如重复的主键)时,我都会看到此日志:
执行 DbCommand 失败(15 毫秒)
我还在 SQL PROFILER 上看到错误,很明显这是主键冲突。
在这些情况下,SaveChangesAsync 不会抛出任何错误!它只是无限期地挂起,导致内存泄漏,最终应用程序崩溃。
我尝试用 try catch 包装 savechanges 无济于事,甚至添加了一个取消令牌以在 2 秒后自动取消,即使这样也没有停止挂起。结果总是一个永无止境的 HTTP 请求,从不 returns 对客户端的响应。
*我也试过运行不带配置等待的savechangesasync,结果是一样的。
上下文和存储库的 DI:
services.AddDbContext<SalesDBContext>(o =>
o.UseSqlServer(appSettings.ConnectionString)
.EnableDetailedErrors(true)
.EnableSensitiveDataLogging()
, ServiceLifetime.Scoped);
services.AddScoped<IRepository, EfRepository>();
控制器方法:
[HttpPost]
public async Task<IActionResult> Post([FromBody] SomeRequest request)
{
if (ModelState.IsValid)
{
var data = await _service.AddAsync(request);
return Ok(data);
}
return BadRequest(ModelState.Values);
}
addasync 是调用方:
public class EfRepository : IRepository
{
private readonly SalesDBContext _dbContext;
public EfRepository(SalesDBContext dbContext)
{
_dbContext = dbContext;
}
public async Task<int> AddAsync<T>(T entity) where T : Entity
{
_dbContext.Set<T>().Add(entity);
return await _dbContext.SaveChangesAsync();
}
}
我通过检查没有违规来解决问题,但这并没有解决目前我无法记录这些错误的根本问题,如果在某处再次发生这种情况,它将破坏网站。
根据请求:整个上下文 class:
public class SalesDBContext : DbContext
{
public DbSet<Entity> Entities { get; set; }
public SalesDBContext(DbContextOptions<SalesDBContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(System.Reflection.Assembly.GetExecutingAssembly());
foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
}
//This is the savechanges being called.
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
int result = await base.SaveChangesAsync(cancellationToken);
//Hangs here on primary key conflict error
return result;
}
}
*上下文的生命周期可能与它无关,我在每个生命周期都重现了这个问题。
调用堆栈如下:
*更多信息:
使用非异步方法保存时也会发生挂起。
在 savechanges 中手动抛出错误时,它会正确冒泡。
在共享相同 DLL 的另一个项目中,SaveChanges 会抛出一个正确冒泡的错误,在异步方法中也是如此。
在评论中,您说您将数据库上下文作为瞬态注入。我建议按范围注入它,这将确保您在整个数据流中的每个请求都使用“相同”的 dbcontext。
使用不同的 dbcontext,就像使用瞬态生命周期一样,在不同的消费者中对您的数据访问流的相同请求可能导致奇怪的行为,像这样
终于找到问题了!!我坐下来开始一次一行地删除代码,直到找到罪魁祸首!
.UseSerilog((hostingContext, loggerConfiguration) => {
loggerConfiguration
.ReadFrom.Configuration(hostingContext.Configuration)
.Enrich.FromLogContext()
.Enrich.WithProperty("ApplicationName", typeof(Program).Assembly.GetName().Name)
.Enrich.WithProperty("Environment", hostingContext.HostingEnvironment);
#if DEBUG
loggerConfiguration.Enrich.WithProperty("DebuggerAttached", Debugger.IsAttached);
#endif
});
Serilog 一直以来都处于艰难时期。没有错误指向日志记录有问题的方向,因为所有日志都有效。所以我只是一个接一个地删除功能,直到我删除它时,挂起停止并且异常开始冒泡。
感谢每一位评论和帮助我的人,非常感谢!
我最终放弃了 Serilog,直接使用带有 ILogging 扩展的 Seq,现在一切正常!
尽管这个问题有很多结果none确实给了我一个明确的答案。
每次我尝试通过 AddAsync 和 SaveChangesAsync 方法插入错误数据(例如重复的主键)时,我都会看到此日志: 执行 DbCommand 失败(15 毫秒)
我还在 SQL PROFILER 上看到错误,很明显这是主键冲突。 在这些情况下,SaveChangesAsync 不会抛出任何错误!它只是无限期地挂起,导致内存泄漏,最终应用程序崩溃。 我尝试用 try catch 包装 savechanges 无济于事,甚至添加了一个取消令牌以在 2 秒后自动取消,即使这样也没有停止挂起。结果总是一个永无止境的 HTTP 请求,从不 returns 对客户端的响应。 *我也试过运行不带配置等待的savechangesasync,结果是一样的。
上下文和存储库的 DI:
services.AddDbContext<SalesDBContext>(o =>
o.UseSqlServer(appSettings.ConnectionString)
.EnableDetailedErrors(true)
.EnableSensitiveDataLogging()
, ServiceLifetime.Scoped);
services.AddScoped<IRepository, EfRepository>();
控制器方法:
[HttpPost]
public async Task<IActionResult> Post([FromBody] SomeRequest request)
{
if (ModelState.IsValid)
{
var data = await _service.AddAsync(request);
return Ok(data);
}
return BadRequest(ModelState.Values);
}
addasync 是调用方:
public class EfRepository : IRepository
{
private readonly SalesDBContext _dbContext;
public EfRepository(SalesDBContext dbContext)
{
_dbContext = dbContext;
}
public async Task<int> AddAsync<T>(T entity) where T : Entity
{
_dbContext.Set<T>().Add(entity);
return await _dbContext.SaveChangesAsync();
}
}
我通过检查没有违规来解决问题,但这并没有解决目前我无法记录这些错误的根本问题,如果在某处再次发生这种情况,它将破坏网站。
根据请求:整个上下文 class:
public class SalesDBContext : DbContext
{
public DbSet<Entity> Entities { get; set; }
public SalesDBContext(DbContextOptions<SalesDBContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(System.Reflection.Assembly.GetExecutingAssembly());
foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
}
//This is the savechanges being called.
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
int result = await base.SaveChangesAsync(cancellationToken);
//Hangs here on primary key conflict error
return result;
}
}
*上下文的生命周期可能与它无关,我在每个生命周期都重现了这个问题。
调用堆栈如下:
*更多信息: 使用非异步方法保存时也会发生挂起。 在 savechanges 中手动抛出错误时,它会正确冒泡。 在共享相同 DLL 的另一个项目中,SaveChanges 会抛出一个正确冒泡的错误,在异步方法中也是如此。
在评论中,您说您将数据库上下文作为瞬态注入。我建议按范围注入它,这将确保您在整个数据流中的每个请求都使用“相同”的 dbcontext。
使用不同的 dbcontext,就像使用瞬态生命周期一样,在不同的消费者中对您的数据访问流的相同请求可能导致奇怪的行为,像这样
终于找到问题了!!我坐下来开始一次一行地删除代码,直到找到罪魁祸首!
.UseSerilog((hostingContext, loggerConfiguration) => {
loggerConfiguration
.ReadFrom.Configuration(hostingContext.Configuration)
.Enrich.FromLogContext()
.Enrich.WithProperty("ApplicationName", typeof(Program).Assembly.GetName().Name)
.Enrich.WithProperty("Environment", hostingContext.HostingEnvironment);
#if DEBUG
loggerConfiguration.Enrich.WithProperty("DebuggerAttached", Debugger.IsAttached);
#endif
});
Serilog 一直以来都处于艰难时期。没有错误指向日志记录有问题的方向,因为所有日志都有效。所以我只是一个接一个地删除功能,直到我删除它时,挂起停止并且异常开始冒泡。
感谢每一位评论和帮助我的人,非常感谢!
我最终放弃了 Serilog,直接使用带有 ILogging 扩展的 Seq,现在一切正常!