web-dev-qa-db-fra.com

La connexion ne prend pas en charge MultipleActiveResultSets lors de l’utilisation de Dapper.SimpleCRUD dans un forEach

J'ai le code suivant:

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

Je reçois le 

InvalidOperationException: la connexion ne prend pas en charge MultipleActiveResultSets

Je ne comprends pas que je suis await dans chaque mise à jour, alors pourquoi cette erreur est-elle générée?.

Remarque: je n'ai aucun contrôle sur la chaîne de connexion, je ne peux donc pas activer MARS.

4
MotKohn

Ce code lance une tâche pour chaque élément de la liste, mais n'attend pas la fin de chaque tâche avant de commencer le suivant. Dans chaque tâche, il attend la fin de la mise à jour. Essayer

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

Qui est équivalent à 

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

Si vous utilisez une méthode non asynchrone, vous devrez attendre (), pas attendre chaque tâche. PAR EXEMPLE

    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
    }
6

Vous devez ajouter l'attribut MultipleActiveResultSets dans la chaîne de connexion et le définir sur true pour autoriser plusieurs ensembles de résultats actifs. 

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

Pour en savoir plus, consultez: https://docs.Microsoft.com/en-us/dotnet/framework/data/adonet/sql/enabling-multiple-active-result-sets

22
vendettamit

MARS a quelques limitations et également une surcharge non nulle. Vous pouvez utiliser les aides suivantes pour rendre les mises à jour séquentielles:

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
}

Donc, votre exemple se transformerait en

await test.WhenAllOneByOne(conn.UpdateAsync);

J'appelle généralement le deuxième assistant au lieu de Task.WhenAll, comme suit:

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