获取连接在forEach中使用Dapper.SimpleCRUD时不支持MultipleActiveResultSets

Mot*_*ohn 6 c# sql-server foreach dapper dapper-simplecrud

我有以下代码:

var test = new FallEnvironmentalCondition[] {
    new FallEnvironmentalCondition {Id=40,FallId=3,EnvironmentalConditionId=1},
    new FallEnvironmentalCondition {Id=41,FallId=3,EnvironmentalConditionId=2},
    new FallEnvironmentalCondition {Id=42,FallId=3,EnvironmentalConditionId=3}
};
test.ToList().ForEach(async x => await conn.UpdateAsync(x));
Run Code Online (Sandbox Code Playgroud)

我得到了

InvalidOperationException:连接不支持MultipleActiveResultSets

我不明白我await正在进行每次更新,所以为什么我会收到此错误.

注意:我无法控制连接字符串,因此无法打开MARS.

ven*_*mit 30

您需要MultipleActiveResultSets在连接字符串中添加属性并将其设置为true以允许多个活动结果集.

 "Data Source=MSSQL1;" & _  
    "Initial Catalog=AdventureWorks;Integrated Security=SSPI;" & _  
    "MultipleActiveResultSets=True"  
Run Code Online (Sandbox Code Playgroud)

欲了解更多信息,访问:https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/enabling-multiple-active-result-sets

  • 在这种情况下,您应该使用非异步方法以顺序方式更新您的实体。 (2认同)
  • 为什么不等待帮忙? (2认同)
  • 因为您的程序将在需要同时访问连接的多个线程中执行更新命令。 (2认同)

Raf*_*eis 11

问题是ForEach方法不是异步方法。它不会等待您的 lambda 返回的任务。运行该代码将触发每个任务,而不是等待其中任何一个完成。

一般要点:将 lambda 标记为异步不会使您传递给它的同步方法以异步方式运行。

解决方案:您将需要使用等待任务完成的 foreach 循环。

例如:foreach (var x in xs) await f(x);

如果您愿意,可以将其包装在辅助方法中。

(我知道这是一个老问题,但我认为没有明确回答)


Dav*_*oft 6

该代码为列表中的每个项启动一个Task,但不等待每个任务在开始下一个任务之前完成.在每个任务中,它等待更新完成.尝试

 Enumerable.Range(1, 10).ToList().ForEach(async i => await Task.Delay(1000).ContinueWith(t => Console.WriteLine(DateTime.Now)));
Run Code Online (Sandbox Code Playgroud)

这相当于

    foreach (var i in Enumerable.Range(1, 10).ToList() )
    {
        var task = Task.Delay(1000).ContinueWith(t => Console.WriteLine(DateTime.Now));
    }
Run Code Online (Sandbox Code Playgroud)

如果您使用的是非异步方法,则必须等待Wait(),而不是等待每个任务.例如

    foreach (var i in Enumerable.Range(1, 10).ToList() )
    {
        var task = Task.Delay(1000).ContinueWith(t => Console.WriteLine(DateTime.Now));
        //possibly do other stuff on this thread
        task.Wait(); //wait for this task to complete
    }
Run Code Online (Sandbox Code Playgroud)


Her*_*Kan 6

MARS 有一些限制,而且开销也非零。您可以使用以下帮助程序使更新按顺序进行:

public static async Task WhenAllOneByOne<T>(this IEnumerable<T> source, Func<T, Task> process)
{
    foreach (var item in source)
        await process(item);
}

public static async Task<List<U>> WhenAllOneByOne<T, U>(this IEnumerable<T> source, Func<T, Task<U>> transform)
{
    var results = new List<U>();

    foreach (var item in source)
        results.Add(await transform(item));

    return results;
    // I would use yield return but unfortunately it is not supported in async methods
}
Run Code Online (Sandbox Code Playgroud)

所以你的例子会变成

await test.WhenAllOneByOne(conn.UpdateAsync);
Run Code Online (Sandbox Code Playgroud)

我通常调用第二个助手而不是Task.WhenAll,如下所示:

await Task.WhenAll(source.Select(transform)); // not MARS-safe
await source.WhenAllOneByOne(transform); // MARS-safe
Run Code Online (Sandbox Code Playgroud)