Comment savoir si un SqlConnection a un SqlDataReader attaché?

C’est quelque chose qui est plus de la curiosité que le but réel. Si vous avez un SqlConnection ouvert et y attachez un SqlDataReader , puis essayez d’exécuter une autre requête en utilisant le même SqlConnection , une erreur sera générée. Ma question est la suivante: comment SqlConnection sait-il qu’un lecteur y est attaché? Il n’y a pas de propriété publique ou quoi que ce soit pour HasDataReader , alors comment la classe SqlConnection -elle au courant?

Question originale: (qui n’est plus pertinente)

Bonjour, je suis en train de mettre en place une petite chose pour le pooling de connexion et l’un des bugs les plus courants que nous avons rencontrés (c’est toujours une solution facile, mais nous ne pouvons pas nous souvenir de reader.Close() !) C’est quand nous avons une connexion utilisée par beaucoup de classes / méthodes et une méthode ouvre un lecteur de données et oublie de le fermer. Ce n’est pas vraiment mauvais parce que souvent, tout ce que vous avez à faire est d’aller dans le débogueur, de monter d’un niveau et de voir la fonction avant qu’elle ne soit, et de vérifier si elle avait un lecteur de données non fermé.

Maintenant, voici le plus gros problème. Dans ce pool de connexions, si un datareader est ouvert, il n’est pas connu jusqu’à ce qu’un thread obtienne une connexion et tente de l’utiliser et que l’élément qui a ouvert à l’origine le lecteur de données ne soit même plus en vie.

Si simplement, comment pouvez-vous détecter si un lecteur de données est ouvert sur une connexion et existe-t-il un moyen de fermer le lecteur sans fermer la connexion?

comment le SqlConnection sait-il qu’un lecteur y est attaché

Autant que je sache, SQLConnection sait qu’un lecteur est connecté, car il en garde une référence interne.

Une utilisation judicieuse de Reflector montre que l’object SQLConnection a un champ privé de type DBConnectionInternal, qui est rempli avec l’une des nombreuses implémentations concrètes de cette classe abstraite. Lorsque vous essayez d’append un deuxième lecteur actif à la connexion, la méthode “ValidateConnectionForExecute” est appelée sur la connexion interne, ce qui mène à l’examen d’un “ReferenceCollection” interne. Lorsque cela révèle un lecteur actif existant, une exception est levée.

Je suppose que si vous le vouliez, vous pourriez creuser tout cela vous-même au moment de l’exécution avec reflection.

Pour vous assurer de fermer vos datareaders (et vos connexions à une firebase database), ouvrez-les toujours dans un bloc using, comme suit:

 using (SqlDataReader rdr = MySqlCommandObject.ExecuteReader()) { while (rdr.Read()) { //... } } // The SqlDataReader is guaranteed to be closed here, even if an exception was thrown. 

Personne n’a vraiment répondu à la question d’Earlz. (“Pourquoi le faites-vous ainsi?” N’est pas une réponse.) Je pense que la réponse est que vous ne pouvez pas savoir si une connexion est associée à un lecteur de données ouvert simplement en regardant la connexion elle-même. La connexion n’expose aucune propriété qui vous le dira. L’ouverture d’une connexion définit sa propriété State sur ConnectionState.Open. Ouvrir un lecteur de données sur celui-ci ne change pas l’état de la connexion. Les valeurs d’état telles que ConnectionState.Fetching sont utilisées uniquement lorsque des opérations de données telles que SqlDataReader.Read () sont en cours. Lorsque la connexion est simplement en attente entre les lectures, l’état de la connexion est simplement ouvert. Donc, pour déterminer quand un lecteur ouvert utilise la connexion, vous devez vérifier l’état des lecteurs susceptibles de l’utiliser.

wow .. Beaucoup de gens ne répondent pas à la question! Ce que personne ne mentionne, ce sont les applications multithreads. Je pense que tout le monde ici comprend le fait que vous devez fermer le lecteur, mais ce que je ne semblais voir personne adresser était le fait que le lecteur pourrait ne pas avoir fini lorsque la prochaine demande arrivera. Par exemple, j’ai une table. qui est rempli via un thread séparé afin que je préserve l’interaction de l’interface utilisateur. Il serait bien d’avoir les deuxième et quasortingème threads en attente pendant l’utilisation de la connexion. Puis, quand il libère, fais-le. Sans moyen précis de déterminer si un lecteur est connecté à la connexion, je dois passer plusieurs minutes à créer une sorte de système de drapeau booléen statique pour chaque lecteur de chaque classe susceptible de vouloir utiliser la connexion. Beaucoup plus complexe que nécessaire

et puis essayez d’exécuter une autre requête en utilisant le même SqlConnection, il générera une erreur.

bien sûr, vous pouvez activer plusieurs ensembles de résultats actifs – alors il ne jette pas . Bien sûr, il y a quelques limitations (n’est-ce pas toujours?), Mais ça va marcher. Bien entendu, cela n’est destiné qu’aux opérations d’ imbrication . Si le problème est que vous avez accidentellement laissé quelque chose ouvert (que vous auriez déjà dû fermer), alors la réponse est (comme déjà indiqué) en using .

Pour éviter cela, placez votre DataReader dans un bloc using, cela garantira qu’il dispose de la connexion de la manière suivante:

 using (IDataReader reader = command.ExecuteReader()) { //do stuff } 

Il existe une propriété sur IDataReader appelée IsClosed qui vous indiquera son état.

Vérifiez si c’est ouvert, et si oui, fermez-le. header-à-tête, si vous utilisez la classe SqlHelper, il s’agit d’un bogue. Il ne ferme pas la connexion dans certains scénarios. La solution consiste à utiliser try / catch ou à utiliser des blocs dans votre code, selon que vous soyez pré-2.0 ou non.

Vous pouvez également utiliser des delegates si, pour une raison quelconque, vous ne pouvez pas utiliser la clause d’utilisation, voici un exemple de la procédure à suivre:

 public delegate void TransactionRunner(DbConnection sender, DbTransaction trans, object state); public void RunTransaction(TransactionRunner runner, object state) { RunTransaction(runner, IsolationLevel.ReadCommitted, state); } public void RunTransaction(TransactionRunner runner, IsolationLevel il, object state) { DbConnection cn = GetConnection from pool DbTransaction trans = null; try { trans = cn.BeginTransaction(il); runner(cn, trans, state); trans.Commit(); } catch (Exception err) { if (trans != null) trans.Rollback(); throw err; } finally { //Here you can close anything that was left open } } 

Ensuite, lorsque vous devez utiliser ceci, utilisez simplement la fonction et transmettez-la en tant que

 public void DoStuff(){ TransactionRunner tr = new TransactionRunner(MyFunction); RunTransaction(tr, ); } public void DoStuffInternal(DbConnection cn, DbTransaction trans, object state){ //Do Stuff and Im sure that the transaction will commit or rollback } 

Cela ressemble à une surdose maintenant dans .Net 3.5, mais c’est ce que nous avions fait à l’époque dans .Net 1.0 … J’espère que cela aide …

Aujourd’hui, je suis aussi tombé dans la même situation mais … pas de chance sur le Web.

Donc, j’ai écrit le code ci-dessous pour savoir si un lecteur est ouvert dans une connexion ou généralement si une connexion est prête à être utilisée:

 private bool IsConnectionReady(SqlConnection Connection) { bool nRet = true; try { Ssortingng sql = "SELECT * FROM dummy_table"; using (SqlCommand cmd = new SqlCommand(sql, Connection)) { using (SqlDataReader rdr = cmd.ExecuteReader()) { } } } catch (Exception ex) { nRet = false; } return nRet; } 

“Dummy_table” est une table factice vide dans ma firebase database pour vérifier l’accessibilité.

Ceci est juste une solution de contournement, mais je devrais faire en sorte que les choses fonctionnent et pouvoir vérifier la disponibilité de la connexion dans tous les cas.

Donc, j’espère que cela vous aide.

Selon l’ article , vous devriez toujours fermer le lecteur une fois que vous avez terminé, même si vous utilisez un bloc using. Un bloc using fermera une connexion, mais ne fermera pas un lecteur. Pourquoi cette incohérence? Me bat.

vient de trébucher sur cette vieille question. J’ai trouvé un article sur la réalisation de ceci via une reflection:

http://blogs.msdn.com/b/dataaccesstechnologies/archive/2009/04/08/how-to-find-out-the-data-reader-referencing-an-ado-net-connection-object-to- réparer-l’erreur-il-ya-déjà-un-ouvrier-de-données-ouvert-associé-à-cette-commande-qui-doit-être-fermé-premier.aspx

Créez un nouvel object de commande avec la même connexion et un nouveau lecteur de données avec le nouvel object de commande créé. Cela fonctionnera bien.