2010-05-19 6 views
4

S'il existe un moyen facile de récupérer une instruction SQL terminée après la substitution de paramètres? Par exemple, je veux conserver un fichier journal de tous les SQL que ce programme exécute.Comment capturer SQL avec les paramètres substitués dans? (.NET, SqlCommand)

Ou si je veux faire cela, est-ce que je veux juste me débarrasser des paramètres, et faire toute la requête à l'ancienne, dans une grande chaîne?

Exemple simple: Je veux capturer la sortie:

SELECT subcatId DE EnrollmentSubCategory OU catid = 1

.. de ce code:

Dim subCatSQL As String = "SELECT subcatId FROM EnrollmentSubCategory WHERE catid = @catId" 
    Dim connectionString As String = "X" 
    Dim conn As New SqlConnection(connectionString) 
    If conn.State = ConnectionState.Closed Then 
     conn.Open() 
    End If 
    Dim cmd As New SqlCommand(subCatSQL, conn) 
    With cmd 
     .Parameters.Add(New SqlParameter("@catId", SqlDbType.Int, 1)) 
    End With 

    Console.WriteLine("Before: " + cmd.CommandText) 
    cmd.Prepare() 
    Console.WriteLine("After: " + cmd.CommandText) 

J'avais supposé Préparer() ferait les substitutions, mais apparemment pas.

Pensées? Conseil? Merci d'avance.

Répondre

6

Non, .Prepare ne fait pas cela, et en fait rien dans la commande le fait. Les valeurs des paramètres ne sont jamais remplacées dans la chaîne de commande réelle. Il est envoyé à la base de données séparément, ce qui est bon pour plusieurs raisons:

  • Cela permet SQLServer (ou d'autres dbs) pour mettre en cache le plan de requête pour votre requête et réutiliser la prochaine fois. Lorsqu'une chaîne de texte de commande différente est envoyée à la base de données à chaque fois, la base de données doit développer un plan à chaque fois. En envoyant les paramètres à la db compartimentée, il y a une défense de nature contre les attaques par injection sql.

Sauf si vous utilisez une base de données vraiment ancienne (serveur SQL 7?), .Prepare() n'est pas nécessaire et ne fait rien pour vous. Il était utile de compiler la requête sur le serveur, mais cela est fait automatiquement pour vous maintenant. Je n'ai pas utilisé .Prepare() depuis longtemps.

Hmmmmm. En regardant here il semble que .Prepare() fait toujours quelque chose pour vous: si des valeurs sont plus grandes que la longueur définie par le paramètre, le .Prepare() tronque la valeur, de sorte que lorsque vous exécutez vous n'obtenez pas l'erreur et la requête réussit. cool.

+0

Merci pour toutes les réponses - J'ai choisi cela comme réponse car elle a expliqué le raisonnement le mieux. Merci a tous! – Bryan

1

Ce qui est envoyé à SQL Server n'est pas des variables substituées par des valeurs, mais une déclaration préparée, de cette façon, il peut être réutilisé

Ouvrez profileur dans SQL Server et vous verrez ce qui est en cours d'exécution n'est pas SELECT subcatId DE EnrollmentSubCategory WHERE catid = 1

2

Les paramètres ne sont pas remplacés. Le sql tel quel, et les paramètres sont eux-mêmes passés en tant que paramètres du système sp_executesql stocké proc

0

Vous pouvez créer un wrapper autour du fournisseur System.Data.SqlClient (* Par exemple, le fournisseur enregistré dans le fichier de configuration sous ... * providerName="System.Data.SqlClient").Essentiellement un proxy d'interception, vous auriez accès à toutes les informations passant par le fournisseur et pourrait siphonner ce dont vous avez besoin, l'agréger et/ou l'enrichir et l'envoyer aux journaux. Ceci est un peu plus avancé mais ouvre la porte à capturer toute une gamme d'informations et pourrait être inséré/remplacé/enlevé comme une couche distincte de préoccupation.

5

J'ai l'habitude d'ajouter une fonction utilitaire à mes projets qui, étant donné un objet DbCommand (la classe parente d'OracleCommand, SqlCommand, etc.) enregistrera les valeurs de requête et de paramètre. Il pourrait ressembler à ceci:

Public Shared Sub LogQuery(ByRef cmd As DbCommand) 
    If cmd.CommandText Is Nothing Or cmd.CommandText.Length = 0 Then 
     Return 
    End If 

    Dim logFile As CLog = New CLog("sql.log") 
    Dim msg As New StringBuilder 

    msg.AppendLine("*** Query ***") 
    msg.AppendLine(cmd.CommandText) 
    msg.AppendLine("*** End Query ***") 
    msg.AppendLine("*** Parameters ***") 

    For Each p As DbParameter In cmd.Parameters 
     msg.AppendLine(String.Format("{0}: {1}", p.ParameterName, p.Value.ToString())) 
    Next 

    msg.AppendLine("*** End Parameters ***") 

    logFile.WriteLog(msg.ToString() & System.Environment.NewLine) 
End Sub 

Pendant que j'écrivais cela, il me est produit qu'au lieu de vous connecter les valeurs des paramètres séparément, vous pouvez faire un peu String.Replace() ment de substituer les valeurs des paramètres dans la requête et puis connectez-le:

For Each p As DbParameter In cmd.Parameters 
    msg = msg.Replace(p.ParameterName, p.Value.ToString()) 
Next 
+0

Je pense que je ferai aussi cela - il accomplit ce que je cherchais, en effet. – Bryan