Obtener la conexión no es compatible con MultipleActiveResultSets cuando se usa Dapper.SimpleCRUD en un forEach

Tengo el siguiente código:

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)); 

Estoy recibiendo el

InvalidOperationException: la conexión no es compatible con MultipleActiveResultSets

No entiendo que estoy await cada actualización, ¿por qué recibo este error?

Nota: no tengo control sobre la cadena de conexión, así que no puedo activar MARS.

Ese código inicia una Tarea para cada elemento de la lista, pero no espera a que se complete cada tarea antes de comenzar la siguiente. Dentro de cada tarea espera a que se complete la actualización. Tratar

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

Que es equivalente a

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

Si se encuentra en un método no asíncrono, tendrá que esperar (), no esperar cada tarea. P.EJ

  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 } 

Debe agregar el atributo MultipleActiveResultSets en la cadena de conexión y establecerlo en verdadero para permitir múltiples conjuntos de resultados activos.

  "Data Source=MSSQL1;" & _ "Initial Catalog=AdventureWorks;Integrated Security=SSPI;" & _ "MultipleActiveResultSets=True" 

Lea más en: https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/enabling-multiple-active-result-sets

MARS tiene algunas limitaciones y también una sobrecarga distinta de cero. Puede utilizar los siguientes ayudantes para que las actualizaciones sean secuenciales:

 public static async Task WhenAllOneByOne(this IEnumerable source, Func process) { foreach (var item in source) await process(item); } public static async Task> WhenAllOneByOne(this IEnumerable source, Func> transform) { var results = new List(); 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 } 

Entonces tu ejemplo se convertiría en

 await test.WhenAllOneByOne(conn.UpdateAsync); 

Por lo general, llamo al segundo ayudante en lugar de Task.WhenAll , de la siguiente manera:

 await Task.WhenAll(source.Select(transform)); // not MARS-safe await source.WhenAllOneByOne(transform); // MARS-safe 

El problema es que el método ForEach no es un método asíncrono. No esperará la tarea devuelta por su lambda. Ejecutar ese código activará todas las tareas y no esperará a que se complete ninguna de ellas.

Punto general: marcar un lambda como async no hace que un método síncrono se transmita de forma asíncrona.

Solución: deberá utilizar un bucle foreach que espera la finalización de las tareas.

por ejemplo: foreach (var x en xs) espera f (x);

Puede envolver eso en un método de ayuda si lo prefiere.

(Sé que es una pregunta antigua, pero no creo que haya sido respondida claramente)

Intereting Posts