Mysql
 sql >> Base de données >  >> RDS >> Mysql

Il existe déjà un DataReader ouvert ... même s'il ne l'est pas

Je soupçonne que c'est le problème, à la fin de la méthode :

this.connectionPool.Putback(sqlConnection);

Vous ne faites que prendre deux éléments de l'itérateur - vous ne complétez donc jamais le while boucle à moins qu'il n'y ait en fait qu'une seule valeur renvoyée par le lecteur. Vous utilisez maintenant LINQ, qui appellera automatiquement Dispose() sur l'itérateur, donc votre using l'instruction disposera toujours du lecteur - mais vous ne remettez pas la connexion dans le pool. Si vous faites cela dans un finally bloquer, je pense que tout ira bien :

var sqlConnection = this.connectionPool.Take();
try
{
    // Other stuff here...

    using (var reader = this.selectWithSourceVectorCommand.ExecuteReader())
    {
        while (reader.Read())
        {
            yield return ReaderToVectorTransition(reader);
        }
    }
}
finally
{
    this.connectionPool.Putback(sqlConnection);
}

Ou idéalement, si votre pool de connexions est votre propre implémentation, faites Take renvoie quelque chose qui implémente IDisposable et renvoie la connexion au pool quand c'est fait.

Voici un programme court mais complet pour montrer ce qui se passe, sans aucune base de données réelle :

using System;
using System.Collections.Generic;
using System.Linq;

class DummyReader : IDisposable
{
    private readonly int limit;
    private int count = -1;
    public int Count { get { return count; } }

    public DummyReader(int limit)
    {
        this.limit = limit;
    }

    public bool Read()
    {
        count++;
        return count < limit;
    }

    public void Dispose()
    {
        Console.WriteLine("DummyReader.Dispose()");
    }
}

class Test
{    
    static IEnumerable<int> FindValues(int valuesInReader)
    {
        Console.WriteLine("Take from the pool");

        using (var reader = new DummyReader(valuesInReader))
        {
            while (reader.Read())
            {
                yield return reader.Count;
            }
        }
        Console.WriteLine("Put back in the pool");
    }

    static void Main()
    {
        var data = FindValues(2).Take(2).ToArray();
        Console.WriteLine(string.Join(",", data));
    }
}

Comme écrit - modélisant la situation avec le lecteur ne trouvant que deux valeurs - la sortie est :

Take from the pool
DummyReader.Dispose()
0,1

Notez que le lecteur est disposé, mais nous n'allons jamais jusqu'à retourner quoi que ce soit de la piscine. Si vous modifiez Main pour modéliser la situation où le lecteur n'a qu'une seule valeur, comme ceci :

var data = FindValues(1).Take(2).ToArray();

Ensuite, nous parcourons le while boucle, donc la sortie change :

Take from the pool
DummyReader.Dispose()
Put back in the pool
0

Je vous suggère de copier mon programme et de l'expérimenter. Assurez-vous de tout comprendre de ce qui se passe... vous pourrez ensuite l'appliquer à votre propre code. Vous voudrez peut-être lire mon article sur détails sur l'implémentation du bloc itérateur aussi.