【问题标题】:EF Core error: The connection does not support MultipleActiveResultSetsEF Core 错误:连接不支持 MultipleActiveResultSets
【发布时间】:2021-01-17 16:13:37
【问题描述】:

我搜索了所有有错误解决方案的链接。但是,它们都不适合我,因为我已经有异步代码并且按照他们的建议做了。

我们拥有基于 .NET Core 3.1 的 Azure Functions。我们使用最新版本的 Entity Framework Core。我们间歇性地收到此错误:

System.InvalidOperationException:连接不支持 MultipleActiveResultSets。

在 Microsoft.Data.SqlClient.SqlCommand.c.b__164_0(Task1 result) at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke()
在 System.Threading.Tasks.Task.c.<.cctor>b__274_0(Object obj)
在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback 回调, 对象状态)
从先前抛出异常的位置结束堆栈跟踪

在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback 回调, 对象状态)
在 System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, 线程 threadPoolThread)
从之前抛出异常的位置结束堆栈跟踪---

在 Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancelToken)
在 Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancelToken)
在 Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject 参数对象,CancellationToken 取消令牌) 在 Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func4 操作,Func4 verifySucceeded, TState state, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func4 操作,Func4 verifySucceeded, TState state, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.AsyncEnumerator.MoveNextAsync() 在 Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 源,CancellationToken cancelToken)

当我们查看 AppInsights 中的日志时,我们发现异常同时发生,并且对于同一个地方的同一个函数具有完全相同的错误。但是,这是针对三个不同的调用 (InvocationId) 但相同的主机实例 (HostInstanceId) 和不同的操作 Id (操作 ID)。期望是对于每个新的调用,新的 dbContext 将被实例化,因为 AddDbContextPool 默认添加作用域 dbContext 作为依赖项。不确定我们是否可以从中扣除任何东西。

以下是我们的实施方法。感谢您对此的任何帮助。提前致谢。

我们在启动文件中使用以下语句将DbContext 添加到服务中:

builder.Services.AddDbContextPool<OurDbContext>(options =>
{
    options.UseSqlServer("connectionstring"), builder =>
    {
       builder.EnableRetryOnFailure(3, TimeSpan.FromSeconds(2), null);
    });
});

OurDbContext 类具有以下构造函数:

public OurDbContext(DbContextOptions<OurDbContext> options)
    : base(options)
{
}

然后我们在不同的存储库中注入OurDbContext 类,这些存储库使用此上下文与 SQL 对话。类似于下面:

public class TypesRepo : RepoBase<Types>, ITypesRepo
{
  public TypesRepo(OurDbContext ourDbContext) : base(ourDbContext)
  {
  }
  
  public async Task RetrieveTypesAsync(List<string> types)
  {
    var records = await RetrieveConditionAsync(x => types.Contains(x.Type));
    return records?.Select(x => new { x.Type, x.TypeId })
                   .ToDictionary(x => x.Type, x => x.TypeId);
  }
}

public abstract class RepoBase<T> where T : class
{
    protected OurDbContext OurDbContext  { get; set; }

    public RepoBase(OurDbContext OurDbContext)
    {
        this.OurDbContext = OurDbContext;
    }

    public async Task<List<T>> RetrieveConditionAsync(Expression<Func<T, bool>> expression)
    {
        return await OurDbContext.Set<T>().Where(expression).AsNoTracking().ToListAsync();
    }
}

我们在Function类中注入上面的Repo类,调用上面的方法如

await _typesRepo.RetrieveAsync()

P.S.:基于以下评论
我认为 dbcontextpool 将重用 dbcontext 实例,如果它的连接不是活动的/未使用的,但不是活动的。

AddDbContext or AddDbContextPool

【问题讨论】:

  • 您的代码中是否使用了任何 ContinueWith?你在使用 System.Data.SqlClient nuget,如果是,是哪个版本?我使用不同的方法完成了相同的操作,其中我有一个 ActivityTrigger,它使用过程执行 SQL 逻辑,每天执行大量的操作
  • 不,我们的代码中根本没有使用 ContinueWith。 System.Data.SqlClient 都不是 nuget。我们使用实体框架核心 3.1.7,而后者又使用 Microsoft.Data.SqlClient 1.1.3。
  • 您是否测试过将MultipleActiveRecordSets=true; 添加到连接字符串中?即使您不想永久使用它,它也会帮助您缩小问题范围。
  • 如果关闭上下文池,异常是否总是消失?是否涉及延迟加载?
  • 不同请求同时执行时间歇性发生。不涉及延迟加载。我的问题中的 Ctrl c+v,--------------当我们查看 AppInsights 中的日志时,我们发现异常同时发生,并且在一样的地方。但是,它是针对三个不同的调用 (InvocationId) 但相同的主机实例 (HostInstanceId) 和不同的操作 Id (Operation ID)。期望对于每个新的调用,新的 dbContext 将被实例化,因为 AddDbContextPool 默认添加作用域 dbContext 作为依赖项.

标签: .net-core entity-framework-core azure-functions ef-core-3.1


【解决方案1】:

您的连接字符串需要指定MultipleActiveRecordSets=true;,以告知 SqlServer 确保在您与其建立的连接上启用此功能。

例如:

Data Source=localhost;Initial Catalog=master;Integrated Security=True;MultipleActiveResultSets=True

更多信息在这里:https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/enabling-multiple-active-result-sets

【讨论】:

  • 是的,我在多个地方读到过,但同时它说如果启用 MARS,则会影响性能。在这种情况下,由于这是三个不同的 dbcontext 实例,每个实例只有一个查询的结果集。不知道为什么会抛出这个错误。
  • 我也不确定为什么在这种情况下我们需要启用 MARS?
  • 这是实体框架的一大好处:透明
  • 我想我现在更了解您的问题 - AddDbContextPool 的全部意义在于共享 DbContexts 和请求之间的连接。但是从您所看到的情况来看,当 DbContext 被传递时,连接上的某些东西似乎仍然处于活动状态,因此来自服务器的 MARS 投诉
  • 或 - DbContexts 共享连接,这意味着交错的 sql 命令
【解决方案2】:

我们最近遇到了类似的问题,这是由于一个请求同时启动了多个线程,并且每个线程都使用相同的 DbContext 来并行化繁重的数据库查询。

此外,我们使用DbReader 处理结果,并锁定连接,直到读取器被处理掉。

这可以防止其他线程重复使用相同的 DbContext / 连接实例,这些线程也使用DbReader 来处理结果。

启用多活动结果集功能为我们解决了这个问题,在我们的例子中,我们实际上并没有注意到这对性能有任何影响。

【讨论】:

    猜你喜欢
    • 2018-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-09
    • 1970-01-01
    • 2021-03-07
    相关资源
    最近更新 更多