oracle / dotnet-db-samples

.NET code samples for Oracle database developers #OracleDotNet
http://otn.oracle.com/dotnet
MIT License
408 stars 190 forks source link

Asynchronous operation throws "A second operation was started on this context instance before a previous operation completed" #383

Closed BehroozBahrameh closed 1 month ago

BehroozBahrameh commented 1 month ago

background

migration from 8.21.140 to 8.23.40


scenario

Bellow execution tried to execute two read requests on the DB from the same repository,

public async Task Foo(){
    var firstTask = dummyRepository.FindAllAsync(100);
    var secondTask = dummyRepository.CountAsync();

    Task.WaitAll(firstTask, secondTask);
}

repository implementation :

public class DummyRepository(DummyContext dbContext, IMapper mapper) : IDummyRepository
{
    public Task<int> CountAsync()
    {
        return dbContext
            .DummyEntities
            .CountAsync();
    }

    public Task<Dummy[]> CountAsync(int limit)
    {
        return mapper.ProjectTo<Dummy>(
                dbContext
                    .DummyEntities
                    .Take(limit))
            .ToArrayAsync();
    }
}

Context registration :

services.AddDbContext<DummyContext>(options =>
  options.UseOracle(
    configuration.GetConnection("ConnectionStrings:Oracle").ToString(),
      builder => builder.UseOracleSQLCompatibility(OracleSQLCompatibility.DatabaseVersion21)
    ));

Issue

The above code works perfectly with 8.21.140 and as soon as you update to 8.23.40 you get bellow exception:

An unhandled exception has occurred while executing the request.
      System.AggregateException: One or more errors occurred. (A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.)
       ---> System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
         at Microsoft.EntityFrameworkCore.Infrastructure.Internal.ConcurrencyDetector.EnterCriticalSection()
         at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
         at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
         --- End of inner exception stack trace ---
         at System.Threading.Tasks.Task.WaitAllCore(Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken)
         at System.Threading.Tasks.Task.WaitAll(Task[] tasks)
alexkeh commented 1 month ago

8.21.140 is the ODP.NET 21c version and 8.23.40 is the ODP.NET 23ai version. Async support was added in ODP.NET 23ai, which is why you can see different behavior when upgrading.

Under the covers, async calls using ODP.NET 21c would be executed synchronously instead.

Back to your original problem. The question then becomes whether this is an EF Core error or ODP.NET error. What I see from the error is a EF Core issue so far. Do you see an ORA error if you handle the exception? Alternatively, you can turn on ODP.NET tracing to see if an ORA error occurs.

Another way to verify is to execute the same code with another EF Core provider, such an npgsql or sqlclient. If the same error occurs, then it's very likely to be an EF Core issue (or not a supported use in EF Core as the error link describes.

BehroozBahrameh commented 1 month ago

Thank you so much for your reply, I wrote a sample app with progress and yes, you are right, the same issue exists.