Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET 5 EF Core SaveChangesAsync hangs on errors

Despite there are many results for this question none have really given me a clear answer.

Every time I try to insert faulty data such as a duplicate primary key, via the AddAsync and SaveChangesAsync methods, I see this log: Failed executing DbCommand (15ms)

I also see the error on the SQL PROFILER and it very clear it's a primary key conflict. In these cases the SaveChangesAsync does not throw any error! It just hangs indefinitely causing a memory leak and eventually the app crashes. I tried wrapping the savechanges with a try catch to no avail, and even added a cancellation token to auto cancel after 2 seconds and even that did not stop the hang. The result is always a never ending HTTP request that never returns a response to the client. *I also tried running the savechangesasync without the configure await and the result is the same.

DI for context and repository:

        services.AddDbContext<SalesDBContext>(o => 
            o.UseSqlServer(appSettings.ConnectionString)
            .EnableDetailedErrors(true)
            .EnableSensitiveDataLogging()
        , ServiceLifetime.Scoped);

services.AddScoped<IRepository, EfRepository>();

The Controller Method:

[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);
}

The addasync is the caller:

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();
    }
}

I fixed the issue by checking there are no violations, but this does not solve the root problem that currently it is impossible for me to log these errors and if this happens again somewhere it will destroy the website.

As per request: The entire context 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;
    }
}

*The lifetime of the context probably has nothing to do with it, I reproduced the issue with every lifetime.

Here is the callstack: Call stack

*More information: The hang occurs also when saving with the non Async method. When manually throwing an error in the savechanges it bubbles up correctly. In another project that shares the same DLLs, the SaveChanges throws an error that bubbles up correctly, also in Async methods.

like image 914
Bodokh Avatar asked Mar 09 '21 12:03

Bodokh


1 Answers

I have finally found the Issue!! I sat down and just started removing code one line at a time until I found the culprit!

.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 has been giving be the hard time all along. There were no errors pointing in the direction that something is wrong with the logging because all the logs work. So I just removed features one after another until when I removed this the hanging stopped and exceptions started to bubble up.

Thank you for every one who commented and tried to help me, it is much appreciated!

I ended up ditching Serilog and using Seq directly with ILogging extension and everything now works fine!

like image 103
Bodokh Avatar answered Nov 03 '22 00:11

Bodokh