2013-07-25 1 views
2

En fait, il ne doit pas nécessairement s'agir d'un IDataReader.Fermeture d'un IDataReader après que le retour de rendement est terminé

J'ai eu une fonction quelque chose comme ceci:

public IEnumerable<MyClass> GetObjects() 
{ 
    IDataReader dr = GetSomeDataReader(); 
    while (dr.Read()) 
    { 
    yield return new MyClass(dr); 
    } 
    CloseDBConnections(); 
} 

Ce fonctionnait très bien jusqu'à ce que je refactorisé comme si:

public IEnumerable<MyClass> GetObjects() 
{ 
    IDataReader dr = GetSomeDataReader(); 
    try 
    { 
    return ProcessReader(dr); 
    } finally { 
    CloseDBConnections(); 
    } 
} 
public IEnumerable<MyClass> ProcessReader(IDataReader dr) 
{ 
    while (dr.Read()) 
    { 
    yield return new MyClass(dr); 
    } 
} 

Cela ne fonctionne pas parce que lorsque le CloseDBConnections() est exécuté l'énumération n'a pas encore été traité.

Appel .ToList() sur le retour de GetObjects est ce qui exécute en fait l'énumération, mais alors la connexion a déjà été détruite et l'IDataReader échoue.

Dans mon exemple CloseDBConnections ne peut pas être appelé à partir de la nouvelle fonction ProcessReader parce que le IDataReader aurait pu venir d'une autre source (le point entier de refactoring en l'occurrence)

est-il une solution raisonnable pour ce ou dois-je dupliquer le code d'énumération?

J'ai essayé de mettre l'appel à ProcessReader comme yield return au lieu de return, mais cela ne fonctionne pas parce que C# (compréhensible) pense que je suis en train d'ajouter un IEnumerable au IEnumerable!

Répondre

3

Que diriez-vous d'appeler CloseDBConnections dans ProcessReader par un rappel?

public IEnumerable<MyClass> GetObjects() 
{ 
    return ProcessReader(GetSomeDataReader(), CloseDBConnections); 
} 

public IEnumerable<MyClass> ProcessReader(IDataReader dr, Action OnFinished) 
{ 
    try 
    { 
    while (dr.Read()) 
     yield return new MyClass(dr); 
    } 
    finally 
    { 
    if (OnFinished != null) 
     OnFinished(); 
    } 
} 
+0

Vous devez exécuter le rappel dans un 'finally'. Sans cela, il ne sera pas exécuté si l'itération se termine tôt (par exemple 'GetObjects(). First()'). – svick

+0

@svick Bon point. – sloth

+0

@DominicKexel Je l'aime. C'est comme ça que je finis par faire beaucoup de choses en Javascript – DJL

3

Pas joli, mais cela devrait fonctionner.

public IEnumerable<MyClass> GetObjects() 
{ 
    IDataReader dr = GetSomeDataReader(); 
    try 
    { 
    foreach (var result in ProcessReader(dr)) 
    { 
     yield return result; 
    } 
    } finally { 
    CloseDBConnections(); 
    } 
} 
+0

Oui, je l'ai considéré. J'espérais une solution plus propre (comme un mot-clé magique ou quelque chose comme ça). Dans ce cas, cela ne vaut guère la peine d'être relancé si la solution ci-dessus est la seule possible. Je suppose que j'espérais probablement trop! – DJL

Questions connexes