I have an EF Core website that is set up in the typical way. This has worked fine.
However, I added a Controller Method that gets hit several times per second and when there are multiple active threads, EF throws an exception. If the requests are a few seconds apart, then it works fine.
I have tried debugging this, but I don't see what I am doing wrong.
Here are the details:
The requests are coming from a single page app, and they are all the same connection. If there is only one request, it works fine. But if there are two requests, then the exception is thrown.
System.IndexOutOfRangeException HResult=0x80131508 Message=Index was outside the bounds of the array. Source=System.Data StackTrace: at System.Data.SqlClient.SqlDataReader.CheckHeaderIsReady(Int32 columnIndex, Boolean permitAsync, String methodName) at System.Data.SqlClient.SqlDataReader.IsDBNull(Int32 i) at Microsoft.EntityFrameworkCore.Storage.Internal.TypedRelationalValueBufferFactory.Create(DbDataReader dataReader) at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable
1.AsyncEnumerator.<BufferlessMoveNext>d__12.MoveNext() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.<ExecuteImplementationAsync>d__31
2.MoveNext() at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.d__312.MoveNext() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable
1.AsyncEnumerator.d__11.MoveNext() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor1.EnumeratorExceptionInterceptor.<MoveNext>d__5.MoveNext() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Linq.AsyncEnumerable.<Aggregate_>d__6
3.MoveNext() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at Traken.Data.Repositories.LookupListItemRepository.d__4.MoveNext() in C:\Dev\Traken.app\Traken.5\Dev\Traken\Data\Repositories\LookupListItemRepository.cs:line 38
The repository which is use:
services.AddScoped<LookupListItemRepository, LookupListItemRepository>();
I have also tried AddTransient
but I get the same error.
services.AddTransient<LookupListItemRepository, LookupListItemRepository>();
The DbContext:
services.AddDbContext<ApplicationDbContext>(optionsBuilder =>
{
optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
options => options.EnableRetryOnFailure());
});
The controller
[HttpGet("{tableName}")]
public async Task<List<LookupListItem>> GetLookupTableAsync(string tableName) => await this._repository.List(tableName);
The Repository
public async Task<List<LookupListItem>> List(string tableName)
{
return await this._dbContext.LookupTables.FromSql(
$@"{SqlStrings.SP.GetLookupTable}
@tableName = {tableName}"
).AsNoTracking().ToListAsync();
}
How do I resolve this?
I resolved this by setting the repository to AddTransient
, instead of AddScoped
services.AddTransient<LookupListItemRepository, LookupListItemRepository>();
and setting the lifetime of the context to Transient
.
services.AddDbContext<ApplicationDbContext>(optionsBuilder =>
{ optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
options => options.EnableRetryOnFailure());
}, ServiceLifetime.Transient);
See Also: https://docs.microsoft.com/en-us/ef/core/querying/async
public async Task<List<Blog>> GetBlogsAsync()
{
using (var context = new BloggingContext())
{
return await context.Blogs.ToListAsync();
}
}
User contributions licensed under CC BY-SA 3.0