【问题标题】:How to use DbContext in multi-threaded app如何在多线程应用程序中使用 DbContext
【发布时间】:2019-05-22 16:56:53
【问题描述】:

我有一个以典型方式设置的 EF Core 网站。这工作得很好。

但是,我添加了一个每秒被命中几次的控制器方法,当有多个活动线程时,EF 会引发异常。如果请求相隔几秒钟,那么它工作正常。

我已经尝试调试这个,但我看不出我做错了什么。

以下是详细信息:

请求来自单页应用,它们都是同一个连接。如果只有一个请求,它工作正常。但是如果有两个请求,则抛出异常。

System.IndexOutOfRangeException H结果=0x80131508 Message=Index 超出了数组的范围。 源=系统.数据 堆栈跟踪: 在 System.Data.SqlClient.SqlDataReader.CheckHeaderIsReady(Int32 columnIndex,布尔 permitAsync,字符串方法名) 在 System.Data.SqlClient.SqlDataReader.IsDBNull (Int32 i) 在 Microsoft.EntityFrameworkCore.Storage.Internal.TypedRelationalValueBufferFactory.Create(DbDataReader dataReader) 在 Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable1.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__312.MoveNext() 在 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.AsyncQueryingEnumerable1.AsyncEnumerator.d__11.MoveNext() 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务) 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在 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__63.MoveNext() 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务) 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() 在 Traken.Data.Repositories.LookupListItemRepository.d__4.MoveNext() 在 C:\Dev\Traken.app\Traken.5\Dev\Traken\Data\Repositories\LookupListItemRepository.cs:line 38

正在使用的存储库:

services.AddScoped&lt;LookupListItemRepository, LookupListItemRepository&gt;();

我也尝试过AddTransient,但我得到了同样的错误。

services.AddTransient&lt;LookupListItemRepository, LookupListItemRepository&gt;();

DbContext:

services.AddDbContext<ApplicationDbContext>(optionsBuilder =>
{
                optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
options => options.EnableRetryOnFailure());
});

控制器

[HttpGet("{tableName}")]
public async Task<List<LookupListItem>> GetLookupTableAsync(string tableName) => await this._repository.List(tableName);

存储库

public async Task<List<LookupListItem>> List(string tableName)
{
    return await this._dbContext.LookupTables.FromSql(
                    $@"{SqlStrings.SP.GetLookupTable} 
                    @tableName = {tableName}"
                ).AsNoTracking().ToListAsync();
}

我该如何解决这个问题?

【问题讨论】:

    标签: entity-framework entity-framework-core entity-framework-core-2.1


    【解决方案1】:

    我通过将存储库设置为AddTransient 而不是AddScoped 解决了这个问题

    services.AddTransient<LookupListItemRepository, LookupListItemRepository>();
    

    并将上下文的生命周期设置为Transient

    services.AddDbContext<ApplicationDbContext>(optionsBuilder =>
    {   optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
            options => options.EnableRetryOnFailure());
    }, ServiceLifetime.Transient);
    

    另请参阅: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();
        }
    }
    

    【讨论】:

    • 虽然使用瞬态生命周期应该可以解决任何此类问题,但这不是必需的。但也不是特别有害。
    • @DavidBrowne-Microsoft 如果没有必要,那么这是什么意思?是否存在应该报告的问题以便修复?
    • 是的,如果您有一个简单的重现,请编辑您的问题以包含它。请求中的所有内容假定按顺序运行,因此请求范围内的 DbContext 永远不会被两个线程同时访问。
    猜你喜欢
    • 1970-01-01
    • 2018-04-28
    • 2010-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多