2010-04-12 6 views
4

Voici un exemple de mon code dans un DAL. Tous les appels aux procédures stockées de la base de données sont structurés de cette façon, et il n'y a pas de SQL en ligne.Ai-je des connexions ADO.NET?

Friend Shared Function Save(ByVal s As MyClass) As Boolean 

    Dim cn As SqlClient.SqlConnection = Dal.Connections.MyAppConnection 
    Dim cmd As New SqlClient.SqlCommand 

    Try 
     cmd.Connection = cn 
     cmd.CommandType = CommandType.StoredProcedure 
     cmd.CommandText = "proc_save_my_class" 

     cmd.Parameters.AddWithValue("@param1", s.Foo) 
     cmd.Parameters.AddWithValue("@param2", s.Bar) 

     Return True 

    Finally 
     Dal.Utility.CleanupAdoObjects(cmd, cn) 
    End Try 

End Function 

est ici l'usine de connexion (si j'utilise le terme correct):

Friend Shared Function MyAppConnection() As SqlClient.SqlConnection 

    Dim cn As New SqlClient.SqlConnection(ConfigurationManager.ConnectionStrings("MyConnectionString").ToString) 
    cn.Open() 

    If cn.State <> ConnectionState.Open Then 
     ' CriticalException is a custom object inheriting from Exception. 
     Throw New CriticalException("Could not connect to the database.") 
    Else 
     Return cn 
    End If 

End Function 

est ici la fonction Dal.Utility.CleaupAdoObjects():

Friend Shared Sub CleanupAdoObjects(ByVal cmd As SqlCommand, ByVal cn As SqlConnection) 
    If cmd IsNot Nothing Then cmd.Dispose() 
    If cn IsNot Nothing AndAlso cn.State <> ConnectionState.Closed Then cn.Close() 
End Sub 

Je suis Le délai d'attente écoulé avant la fin de l'opération ou le serveur ne répond pas. messages d'erreur signalés par les utilisateurs. La DAL de l'application ouvre une connexion, lit ou enregistre des données et les ferme. Aucune connexion n'est jamais laissée ouverte - intentionnellement!

Il n'y a rien d'évident sur le serveur Windows 2000 hébergeant SQL Server 2000 qui indiquerait un problème. Rien dans les journaux des événements et rien dans les journaux SQL Server. Les délais d'expiration sont aléatoires. Je ne peux pas les reproduire. Cela arrive tôt dans la journée avec seulement 1 à 5 utilisateurs dans le système. Cela arrive aussi avec environ 50 utilisateurs dans le système. La plupart des connexions à SQL Server via Performance Monitor, pour toutes les bases de données, a été d'environ 74.

Les dépassements de délai se produisent dans un code qui enregistre et lit dans la base de données dans différentes parties de l'application. La trace de pile ne pointe pas vers une ou deux fonctions DAL incriminées. C'est arrivé dans beaucoup d'endroits différents.

Est-ce que mon code ADO.NET semble être capable de fuir les connexions? Je me suis un peu amusé, et j'ai lu que si le pool de connexion se remplissait, cela peut arriver. Cependant, je ne définis pas explicitement de regroupement de connexions. J'ai même essayé d'augmenter le délai d'attente de connexion dans la chaîne de connexion, mais les délais d'attente arrive bien avant les 300 secondes (5 minutes) Valeur:

<add name="MyConnectionString" connectionString="Data Source=MyServer;Initial Catalog=MyDatabase;Integrated Security=SSPI;Connection Timeout=300;"/> 

Je suis à une perte totale déjà à ce qui est l'origine de ces Problèmes de dépassement de délai Toutes les idées sont appréciées.

EDIT: Il s'agit d'une application WinForms.

Répondre

2

De here:

Contrairement Finaliser, les développeurs doivent appeler Dispose explicitement à libérer des ressources non gérés. En fait, vous devez appeler explicitement la méthode Dispose sur tout objet qui l'implémente pour libérer les ressources non managées pour lesquelles l'objet peut contenir des références.

SqlConnection, SqlCommand, SqlDataReader, etc ... tout mettre en œuvre IDisposable. Si vous insérez toutes ces instances dans un bloc Using, alors Dispose sera appelé automatiquement, vos connexions seront fermées, et vous n'aurez plus à vous soucier de problèmes comme ceux-ci. Incendie de réflecteur et jeter un oeil pour vous-même: (SqlConnection.Dispose)

protected override void Dispose(bool disposing) 
{ 
    if (disposing) 
    { 
     this._userConnectionOptions = null; 
     this._poolGroup = null; 
     this.Close(); 
    } 
    this.DisposeMe(disposing); 
    base.Dispose(disposing); 
} 

Cela rend également le code plus court dans ce que vous ne devez pas ajouter manuellement un bloc Enfin, pour nettoyer vos objets ADO.NET.

Using connection As SqlConnection("your connection string") 
    Using command As New SqlCommand("your sql", connection) 
     connection.Open() 
     Using dataReader As SqlDataReader = command.ExecuteReader() 
      'Your stuff here 
     End Using 
    End Using 
End Using 

En utilisant les forces d'approche Using vous de garder vos objets ADO.NET local, qui pour moi est une bonne chose.

+0

Existe-t-il un équivalent VB.NET à Using? Je ne pense pas que VB.NET's With ... End With fonctionne comme C# 's Using. EDIT: Peut-être que je devrais lire votre code posté plus près ... VB.NET Using;) – HardCode

+0

Si j'ouvre ma connexion dans ma fonction "usine de connexion", et puis enfermer la variable de connexion locale dans un Using (tout en étant déjà ouvert), J'élimine l'ouverture de la connexion après "Utilisation de la commande". Will "Using connection" ferme-t-il toujours la connexion, même si elle a été ouverte dans la fonction d'usine de connexion? – HardCode

+0

VB.NET a l'instruction Using à partir de la structure 2.0. L'utilisation se traduit simplement par un bloc Try Finally, avec Dispose étant appelé dans le Final. En utilisant Reflector, nous pouvons voir que la méthode Dispose de SqlConnection appelle Close(). Peu importe que l'objet soit créé dans votre classe d'usine. Il sera enveloppé dans un bloc Finally et Close sera appelé. C'est la beauté de l'utilisation. Il s'assure que vos objets appellent Dispose(), ce qui dans ce cas garantit également que votre connexion sera fermée. –

1

Vous devez toujours mettre au rebut la connexion, quel que soit son état:

'If cn IsNot Nothing AndAlso cn.State <> ConnectionState.Closed Then cn.Close() 
If cn IsNot Nothing Then cn.Dispose() 

Je ne suis pas sûr qu'il cela pourrait causer vos délais d'attente, mais il est certainement une amélioration.

+0

J'ai envoyé un courriel à Scott Guthrie à ce sujet une fois, qui a renvoyé le problème à quelqu'un d'autre chez Microsoft. Le résultat était que .Close() appelle implicitement .Dispose(). Cependant, penses-tu que ma vérification sur cn.State pourrait causer des problèmes? – HardCode

+0

Oui, la directive est: Fermer == Disposer, et donc une question de goût. Mais vous pouvez conserver une connexion fermée d'être renvoyé à la piscine. –

+1

Je modifie le code pour utiliser Dispose(), et comme Andomar suggéré, définissez la taille maximale du pool à deux (expire en raison du pool de connexion, qui n'est pas l'erreur signalée par les utilisateurs), puis à trois qui ne jette pas une erreur de pool de connexion. J'ouvre et ferme plusieurs connexions consécutivement, mais aucune dans ce cas n'est simultanée. – HardCode

0

Comment vos procédures stockées fonctionnent-elles en dehors de votre application? Que se passe-t-il si vous avez déplacé le 'return true' de votre bloc try/finally dans Save()?

Surveillez les connexions DB dans le moniteur Perf pour voir si elles se développent.

Mais le premier endroit que je voudrais regarder est vos sprocs eux-mêmes - accèdent-ils aux tables dans un ordre cohérent ou pourriez-vous courir dans les verrous? Par exemple, si proc1 manipule table1 puis table2, tandis que proc2 frappe table2 puis table1, vous risquez de rencontrer des problèmes de verrouillage.

+0

Les procédures stockées s'exécutent instantanément - principalement des opérations CRUD. J'ai explicitement défini le délai d'expiration de la commande beaucoup plus haut pour les SP qui effectuent des recherches dans la base de données, etc. Ces délais d'attente se produisent sur les requêtes instantanées «ajouter une nouvelle ligne à une table». – HardCode

3

Une façon de vérifier les fuites de connexion est d'ajouter max pool size à la chaîne de connexion, comme: (. Ou de la taille 2 si l'application utilise deux connexions simultanément)

"integrated security=SSPI;server=MyHost;Max Pool Size=1;" 

En développement je lance habituellement avec ce paramètre

+0

Très bonne idée. J'utilise seulement au plus deux connexions. La fonction de recherche s'exécute sur un contrôle BackgroundWorder. – HardCode

0

Est-ce une application Windows ou une application Web?

Est-ce que les délais d'attente se produisent avec des procédures stockées simples ou simplement avec des procédures plus complexes?

Avez-vous essayé d'exécuter une trace de profileur sql pour voir si les requêtes prennent vraiment beaucoup de temps?

Aussi, avez-vous essayé de convertir à la syntaxe "using", ce qui garantit que les objets sont fermés et éliminés correctement?

+0

C'est une application Windows. J'ai couru le profileur de SQL pour les événements défavorables tels que des erreurs sur des connexions, mais rien n'a montré. Je ne peux pas utiliser "using" car c'est VB.NET. – HardCode

+0

Si vous utilisez Visual Studio 2005 ou supérieur, utiliser est un mot clé VB. Je l'utilise tout le temps. – Jeremy

+0

Je me demande si vous avez des problèmes de threading. Vous avez mentionné que certaines fonctionnalités de recherche sont en cours dans un worker Background.C'est juste une intuition, mais je pense que Partagé et multi-thread ne jouent pas bien ensemble. ADO.NET ne peut pas lire à partir de 2 connexions simultanément sauf si MARS est activé. – Jeremy

Questions connexes