Obtener la statement SQL generada desde un objeto SqlCommand?

Tengo el siguiente código:

Using cmd As SqlCommand = Connection.CreateCommand cmd.CommandText = "UPDATE someTable SET Value = @Value" cmd.CommandText &= " WHERE Id = @Id" cmd.Parameters.AddWithValue("@Id", 1234) cmd.Parameters.AddWithValue("@Value", "myValue") cmd.ExecuteNonQuery End Using 

Me pregunto si hay alguna forma de obtener la statement SQL final como una cadena, que debería verse así:

 UPDATE someTable SET Value = "myValue" WHERE Id = 1234 

Si alguien se pregunta por qué haría esto:

  • para las declaraciones de registro (fallidas)
  • para tener la posibilidad de copiarlo y pegarlo al Enterprise Manager con fines de prueba

Si bien no es perfecto, aquí hay algo que noqueé para TSQL: podría ser ajustado fácilmente para otros sabores … Si nada más le dará un punto de partida para sus propias mejoras 🙂

Esto hace un trabajo OK en tipos de datos y parámetros de salida, etc. similar al uso de “ejecutar procedimiento almacenado” en SSMS. Utilizamos principalmente SP, por lo que el comando “text” no tiene en cuenta los parámetros, etc.

  public static String ParameterValueForSQL(this SqlParameter sp) { String retval = ""; switch (sp.SqlDbType) { case SqlDbType.Char: case SqlDbType.NChar: case SqlDbType.NText: case SqlDbType.NVarChar: case SqlDbType.Text: case SqlDbType.Time: case SqlDbType.VarChar: case SqlDbType.Xml: case SqlDbType.Date: case SqlDbType.DateTime: case SqlDbType.DateTime2: case SqlDbType.DateTimeOffset: retval = "'" + sp.Value.ToString().Replace("'", "''") + "'"; break; case SqlDbType.Bit: retval = (sp.Value.ToBooleanOrDefault(false)) ? "1" : "0"; break; default: retval = sp.Value.ToString().Replace("'", "''"); break; } return retval; } public static String CommandAsSql(this SqlCommand sc) { StringBuilder sql = new StringBuilder(); Boolean FirstParam = true; sql.AppendLine("use " + sc.Connection.Database + ";"); switch (sc.CommandType) { case CommandType.StoredProcedure: sql.AppendLine("declare @return_value int;"); foreach (SqlParameter sp in sc.Parameters) { if ((sp.Direction == ParameterDirection.InputOutput) || (sp.Direction == ParameterDirection.Output)) { sql.Append("declare " + sp.ParameterName + "\t" + sp.SqlDbType.ToString() + "\t= "); sql.AppendLine(((sp.Direction == ParameterDirection.Output) ? "null" : sp.ParameterValueForSQL()) + ";"); } } sql.AppendLine("exec [" + sc.CommandText + "]"); foreach (SqlParameter sp in sc.Parameters) { if (sp.Direction != ParameterDirection.ReturnValue) { sql.Append((FirstParam) ? "\t" : "\t, "); if (FirstParam) FirstParam = false; if (sp.Direction == ParameterDirection.Input) sql.AppendLine(sp.ParameterName + " = " + sp.ParameterValueForSQL()); else sql.AppendLine(sp.ParameterName + " = " + sp.ParameterName + " output"); } } sql.AppendLine(";"); sql.AppendLine("select 'Return Value' = convert(varchar, @return_value);"); foreach (SqlParameter sp in sc.Parameters) { if ((sp.Direction == ParameterDirection.InputOutput) || (sp.Direction == ParameterDirection.Output)) { sql.AppendLine("select '" + sp.ParameterName + "' = convert(varchar, " + sp.ParameterName + ");"); } } break; case CommandType.Text: sql.AppendLine(sc.CommandText); break; } return sql.ToString(); } 

esto genera salida a lo largo de estas líneas …

 use dbMyDatabase; declare @return_value int; declare @OutTotalRows BigInt = null; exec [spMyStoredProc] @InEmployeeID = 1000686 , @InPageSize = 20 , @InPage = 1 , @OutTotalRows = @OutTotalRows output ; select 'Return Value' = convert(varchar, @return_value); select '@OutTotalRows' = convert(varchar, @OutTotalRows); 

Para fines de registro, me temo que no hay una forma más agradable de hacerlo, sino construir usted mismo la cadena:

 string query = cmd.CommandText; foreach (SqlParameter p in cmd.Parameters) { query = query.Replace(p.ParameterName, p.Value.ToString()); } 

Lo siento, lo olvidé … p.Value.ToString() debería hacer el trabajo.

No se puede, porque no genera ningún SQL.

La consulta parametrizada (la de CommandText ) se envía al SQL Server como el equivalente de una instrucción preparada. Cuando ejecuta el comando, los parámetros y el texto de consulta se tratan por separado. En ningún momento se genera una cadena SQL completa.

Puede usar el Analizador de SQL para echar un vistazo entre bastidores.

Necesitaba un comando similar para el transformador de cadena para permitir un registro más detallado, así que escribí este. Producirá el texto necesario para volver a ejecutar el comando en una nueva sesión, incluidos los parámetros de salida y los parámetros estructurados. Está ligeramente probado, pero caveat emptor.

Ejemplo:

 SqlCommand cmd = new SqlCommand("GetEntity", con); cmd.Parameters.AddWithValue("@foobar", 1); cmd.Parameters.Add(new SqlParameter(){ ParameterName = "@outParam", Direction = ParameterDirection.Output, SqlDbType = System.Data.SqlDbType.Int }); cmd.Parameters.Add(new SqlParameter(){ Direction = ParameterDirection.ReturnValue }); cmd.CommandType = CommandType.StoredProcedure; 

Producirá:

 -- BEGIN COMMAND DECLARE @foobar INT = 1; DECLARE @outParam INT = NULL; DECLARE @returnValue INT; -- END PARAMS EXEC @returnValue = GetEntity @foobar = @foobar, @outParam = @outParam OUTPUT -- RESULTS SELECT 1 as Executed, @returnValue as ReturnValue, @outParam as [@outParam]; -- END COMMAND 

Implementación:

 public class SqlCommandDumper { public static string GetCommandText(SqlCommand sqc) { StringBuilder sbCommandText = new StringBuilder(); sbCommandText.AppendLine("-- BEGIN COMMAND"); // params for (int i = 0; i < sqc.Parameters.Count; i++) logParameterToSqlBatch(sqc.Parameters[i], sbCommandText); sbCommandText.AppendLine("-- END PARAMS"); // command if (sqc.CommandType == CommandType.StoredProcedure) { sbCommandText.Append("EXEC "); bool hasReturnValue = false; for (int i = 0; i < sqc.Parameters.Count; i++) { if (sqc.Parameters[i].Direction == ParameterDirection.ReturnValue) hasReturnValue = true; } if (hasReturnValue) { sbCommandText.Append("@returnValue = "); } sbCommandText.Append(sqc.CommandText); bool hasPrev = false; for (int i = 0; i < sqc.Parameters.Count; i++) { var cParam = sqc.Parameters[i]; if (cParam.Direction != ParameterDirection.ReturnValue) { if (hasPrev) sbCommandText.Append(", "); sbCommandText.Append(cParam.ParameterName); sbCommandText.Append(" = "); sbCommandText.Append(cParam.ParameterName); if (cParam.Direction.HasFlag(ParameterDirection.Output)) sbCommandText.Append(" OUTPUT"); hasPrev = true; } } } else { sbCommandText.AppendLine(sqc.CommandText); } sbCommandText.AppendLine("-- RESULTS"); sbCommandText.Append("SELECT 1 as Executed"); for (int i = 0; i < sqc.Parameters.Count; i++) { var cParam = sqc.Parameters[i]; if (cParam.Direction == ParameterDirection.ReturnValue) { sbCommandText.Append(", @returnValue as ReturnValue"); } else if (cParam.Direction.HasFlag(ParameterDirection.Output)) { sbCommandText.Append(", "); sbCommandText.Append(cParam.ParameterName); sbCommandText.Append(" as ["); sbCommandText.Append(cParam.ParameterName); sbCommandText.Append(']'); } } sbCommandText.AppendLine(";"); sbCommandText.AppendLine("-- END COMMAND"); return sbCommandText.ToString(); } private static void logParameterToSqlBatch(SqlParameter param, StringBuilder sbCommandText) { sbCommandText.Append("DECLARE "); if (param.Direction == ParameterDirection.ReturnValue) { sbCommandText.AppendLine("@returnValue INT;"); } else { sbCommandText.Append(param.ParameterName); sbCommandText.Append(' '); if (param.SqlDbType != SqlDbType.Structured) { logParameterType(param, sbCommandText); sbCommandText.Append(" = "); logQuotedParameterValue(param.Value, sbCommandText); sbCommandText.AppendLine(";"); } else { logStructuredParameter(param, sbCommandText); } } } private static void logStructuredParameter(SqlParameter param, StringBuilder sbCommandText) { sbCommandText.AppendLine(" {List Type};"); var dataTable = (DataTable)param.Value; for (int rowNo = 0; rowNo < dataTable.Rows.Count; rowNo++) { sbCommandText.Append("INSERT INTO "); sbCommandText.Append(param.ParameterName); sbCommandText.Append(" VALUES ("); bool hasPrev = true; for (int colNo = 0; colNo < dataTable.Columns.Count; colNo++) { if (hasPrev) { sbCommandText.Append(", "); } logQuotedParameterValue(dataTable.Rows[rowNo].ItemArray[colNo], sbCommandText); hasPrev = true; } sbCommandText.AppendLine(");"); } } const string DATETIME_FORMAT_ROUNDTRIP = "o"; private static void logQuotedParameterValue(object value, StringBuilder sbCommandText) { try { if (value == null) { sbCommandText.Append("NULL"); } else { value = unboxNullable(value); if (value is string || value is char || value is char[] || value is System.Xml.Linq.XElement || value is System.Xml.Linq.XDocument) { sbCommandText.Append("N'"); sbCommandText.Append(value.ToString().Replace("'", "''")); sbCommandText.Append('\''); } else if (value is bool) { // True -> 1, False -> 0 sbCommandText.Append(Convert.ToInt32(value)); } else if (value is sbyte || value is byte || value is short || value is ushort || value is int || value is uint || value is long || value is ulong || value is float || value is double || value is decimal) { sbCommandText.Append(value.ToString()); } else if (value is DateTime) { // SQL Server only supports ISO8601 with 3 digit precision on datetime, // datetime2 (>= SQL Server 2008) parses the .net format, and will // implicitly cast down to datetime. // Alternatively, use the format string "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK" // to match SQL server parsing sbCommandText.Append("CAST('"); sbCommandText.Append(((DateTime)value).ToString(DATETIME_FORMAT_ROUNDTRIP)); sbCommandText.Append("' as datetime2)"); } else if (value is DateTimeOffset) { sbCommandText.Append('\''); sbCommandText.Append(((DateTimeOffset)value).ToString(DATETIME_FORMAT_ROUNDTRIP)); sbCommandText.Append('\''); } else if (value is Guid) { sbCommandText.Append('\''); sbCommandText.Append(((Guid)value).ToString()); sbCommandText.Append('\''); } else if (value is byte[]) { var data = (byte[])value; if (data.Length == 0) { sbCommandText.Append("NULL"); } else { sbCommandText.Append("0x"); for (int i = 0; i < data.Length; i++) { sbCommandText.Append(data[i].ToString("h2")); } } } else { sbCommandText.Append("/* UNKNOWN DATATYPE: "); sbCommandText.Append(value.GetType().ToString()); sbCommandText.Append(" *" + "/ N'"); sbCommandText.Append(value.ToString()); sbCommandText.Append('\''); } } } catch (Exception ex) { sbCommandText.AppendLine("/* Exception occurred while converting parameter: "); sbCommandText.AppendLine(ex.ToString()); sbCommandText.AppendLine("*/"); } } private static object unboxNullable(object value) { var typeOriginal = value.GetType(); if (typeOriginal.IsGenericType && typeOriginal.GetGenericTypeDefinition() == typeof(Nullable<>)) { // generic value, unboxing needed return typeOriginal.InvokeMember("GetValueOrDefault", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.InvokeMethod, null, value, null); } else { return value; } } private static void logParameterType(SqlParameter param, StringBuilder sbCommandText) { switch (param.SqlDbType) { // variable length case SqlDbType.Char: case SqlDbType.NChar: case SqlDbType.Binary: { sbCommandText.Append(param.SqlDbType.ToString().ToUpper()); sbCommandText.Append('('); sbCommandText.Append(param.Size); sbCommandText.Append(')'); } break; case SqlDbType.VarChar: case SqlDbType.NVarChar: case SqlDbType.VarBinary: { sbCommandText.Append(param.SqlDbType.ToString().ToUpper()); sbCommandText.Append("(MAX /* Specified as "); sbCommandText.Append(param.Size); sbCommandText.Append(" */)"); } break; // fixed length case SqlDbType.Text: case SqlDbType.NText: case SqlDbType.Bit: case SqlDbType.TinyInt: case SqlDbType.SmallInt: case SqlDbType.Int: case SqlDbType.BigInt: case SqlDbType.SmallMoney: case SqlDbType.Money: case SqlDbType.Decimal: case SqlDbType.Real: case SqlDbType.Float: case SqlDbType.Date: case SqlDbType.DateTime: case SqlDbType.DateTime2: case SqlDbType.DateTimeOffset: case SqlDbType.UniqueIdentifier: case SqlDbType.Image: { sbCommandText.Append(param.SqlDbType.ToString().ToUpper()); } break; // Unknown case SqlDbType.Timestamp: default: { sbCommandText.Append("/* UNKNOWN DATATYPE: "); sbCommandText.Append(param.SqlDbType.ToString().ToUpper()); sbCommandText.Append(" *" + "/ "); sbCommandText.Append(param.SqlDbType.ToString().ToUpper()); } break; } } } 

Si está utilizando SQL Server, puede usar el Analizador de SQL Server (si lo tiene) para ver la cadena de comandos que realmente se ejecuta. Eso sería útil para copiar / pegar pruebas, pero no para registrar, me temo.

También tuve este problema en el que algunas consultas parametrizadas o sp me daban una SqlException (la mayoría de las cadenas o datos binarios se truncaban), y las sentencias eran difíciles de depurar (hasta donde sé, actualmente no hay soporte para sql-profiler para SQL Azure)

Veo mucho código simular en las reacciones aquí. Terminé poniendo mi solución en un proyecto Sql-Library para uso futuro.

El generador está disponible aquí: https://github.com/jeroenpot/SqlHelper/blob/master/Source/Mirabeau.MsSql.Library/SqlGenerator.cs

Es compatible con CommandType.Text y CommandType.StoredProcedure

Y si instala el paquete nuget, puede generarlo con esta statement:

 SqlDebugHelper.CreateExecutableSqlStatement(sql, parameters); 

Profiler es su mejor opción.

Es posible que deba copiar un conjunto de instrucciones del generador de perfiles debido a los pasos de preparación + ejecución involucrados.

Utilicé parte del código de Flapper para mi solución, que devuelve toda la cadena SQL, incluidos los valores de los parámetros, para ejecutarlos en MS SQL SMS.

 public string ParameterValueForSQL(SqlParameter sp) { string retval = ""; switch (sp.SqlDbType) { case SqlDbType.Char: case SqlDbType.NChar: case SqlDbType.NText: case SqlDbType.NVarChar: case SqlDbType.Text: case SqlDbType.Time: case SqlDbType.VarChar: case SqlDbType.Xml: case SqlDbType.Date: case SqlDbType.DateTime: case SqlDbType.DateTime2: case SqlDbType.DateTimeOffset: if (sp.Value == DBNull.Value) { retval = "NULL"; } else { retval = "'" + sp.Value.ToString().Replace("'", "''") + "'"; } break; case SqlDbType.Bit: if (sp.Value == DBNull.Value) { retval = "NULL"; } else { retval = ((bool)sp.Value == false) ? "0" : "1"; } break; default: if (sp.Value == DBNull.Value) { retval = "NULL"; } else { retval = sp.Value.ToString().Replace("'", "''"); } break; } return retval; } public string CommandAsSql(SqlCommand sc) { string sql = sc.CommandText; sql = sql.Replace("\r\n", "").Replace("\r", "").Replace("\n", ""); sql = System.Text.RegularExpressions.Regex.Replace(sql, @"\s+", " "); foreach (SqlParameter sp in sc.Parameters) { string spName = sp.ParameterName; string spValue = ParameterValueForSQL(sp); sql = sql.Replace(spName, spValue); } sql = sql.Replace("= NULL", "IS NULL"); sql = sql.Replace("!= NULL", "IS NOT NULL"); return sql; } 

Si solo se trata de comprobar cómo se formatea un parámetro en la consulta resultante, la mayoría de los DBMS permitirán consultar literales desde nada. Así:

 Using cmd As SqlCommand = Connection.CreateCommand cmd.CommandText = "SELECT @Value" cmd.Parameters.AddWithValue("@Value", "myValue") Return cmd.ExecuteScalar End Using 

De esta forma, puede ver si las cotizaciones se duplican, etc.

Esto es lo que uso para generar listas de parámetros para un procedimiento almacenado en la consola de depuración:

 string query = (from SqlParameter p in sqlCmd.Parameters where p != null where p.Value != null select string.Format("Param: {0} = {1}, ", p.ParameterName, p.Value.ToString())).Aggregate(sqlCmd.CommandText, (current, parameter) => current + parameter); Debug.WriteLine(query); 

Esto generará una salida de consola similar a esto:

 Customer.prGetCustomerDetails: @Offset = 1, Param: @Fetch = 10, Param: @CategoryLevel1ID = 3, Param: @VehicleLineID = 9, Param: @SalesCode1 = bce, 

Coloco este código directamente debajo de cualquier procedimiento que deseo depurar y es similar a una sesión de generador de perfiles sql pero en C #.

Versión modificada de la respuesta de Kon, ya que solo funciona parcialmente con parámetros nombrados similares. El lado negativo de utilizar la función de reemplazo de cadena. Aparte de eso, le doy pleno crédito en la solución.

  private string GetActualQuery(SqlCommand sqlcmd) { string query = sqlcmd.CommandText; string parameters = ""; string[] strArray = System.Text.RegularExpressions.Regex.Split(query, " VALUES "); //Reconstructs the second half of the SQL Command parameters = "("; int count = 0; foreach (SqlParameter p in sqlcmd.Parameters) { if (count == (sqlcmd.Parameters.Count - 1)) { parameters += p.Value.ToString(); } else { parameters += p.Value.ToString() + ", "; } count++; } parameters += ")"; //Returns the string recombined. return strArray[0] + " VALUES " + parameters; } 

Mi solución:

 public static class DbHelper { public static string ToString(this DbParameterCollection parameters, string sqlQuery) { return parameters.Cast().Aggregate(sqlQuery, (current, p) => current.Replace(p.ParameterName, p.Value.ToString())); } } 

Esta solución funciona para mí ahora mismo. Tal vez es útil para alguien. Disculpe toda la redundancia.

  Public Shared Function SqlString(ByVal cmd As SqlCommand) As String Dim sbRetVal As New System.Text.StringBuilder() For Each item As SqlParameter In cmd.Parameters Select Case item.DbType Case DbType.String sbRetVal.AppendFormat("DECLARE {0} AS VARCHAR(255)", item.ParameterName) sbRetVal.AppendLine() sbRetVal.AppendFormat("SET {0} = '{1}'", item.ParameterName, item.Value) sbRetVal.AppendLine() Case DbType.DateTime sbRetVal.AppendFormat("DECLARE {0} AS DATETIME", item.ParameterName) sbRetVal.AppendLine() sbRetVal.AppendFormat("SET {0} = '{1}'", item.ParameterName, item.Value) sbRetVal.AppendLine() Case DbType.Guid sbRetVal.AppendFormat("DECLARE {0} AS UNIQUEIDENTIFIER", item.ParameterName) sbRetVal.AppendLine() sbRetVal.AppendFormat("SET {0} = '{1}'", item.ParameterName, item.Value) sbRetVal.AppendLine() Case DbType.Int32 sbRetVal.AppendFormat("DECLARE {0} AS int", item.ParameterName) sbRetVal.AppendLine() sbRetVal.AppendFormat("SET {0} = {1}", item.ParameterName, item.Value) sbRetVal.AppendLine() Case Else Stop End Select Next sbRetVal.AppendLine("") sbRetVal.AppendLine(cmd.CommandText) Return sbRetVal.ToString() End Function 

Tenía la misma pregunta exacta y después de leer estas respuestas decidí equivocadamente que no era posible obtener la consulta resultante exacta. Estaba equivocado.

Solución: Abra el Activity Monitor en SQL Server Management Studio , limite la sección de procesos al nombre de usuario de inicio de sesión, la base de datos o el nombre de la aplicación que su aplicación utiliza en la cadena de conexión. Cuando se realiza la llamada al db refresh Activity Monitor . Cuando vea el proceso, haga clic derecho sobre él y View Details .

Tenga en cuenta que esta puede no ser una opción viable para un db ocupado. Pero deberías poder reducir el resultado considerablemente usando estos pasos.

Como se mencionó @pkExec y @Alok, el uso de Reemplazar no funciona en el 100% de los casos. Esta es la solución que he usado en nuestro DAL que usa RegExp para “emparejar palabras completas” solamente y formatear los tipos de datos correctamente. Por lo tanto, el SQL generado se puede probar directamente en MySQL Workbench (o SQLSMS, etc …) 🙂

(Reemplace la función MySQLHelper.EscapeString () según el DBMS utilizado).

 Dim query As String = cmd.CommandText query = query.Replace("SET", "SET" & vbNewLine) query = query.Replace("WHERE", vbNewLine & "WHERE") query = query.Replace("GROUP BY", vbNewLine & "GROUP BY") query = query.Replace("ORDER BY", vbNewLine & "ORDER BY") query = query.Replace("INNER JOIN", vbNewLine & "INNER JOIN") query = query.Replace("LEFT JOIN", vbNewLine & "LEFT JOIN") query = query.Replace("RIGHT JOIN", vbNewLine & "RIGHT JOIN") If query.Contains("UNION ALL") Then query = query.Replace("UNION ALL", vbNewLine & "UNION ALL" & vbNewLine) ElseIf query.Contains("UNION DISTINCT") Then query = query.Replace("UNION DISTINCT", vbNewLine & "UNION DISTINCT" & vbNewLine) Else query = query.Replace("UNION", vbNewLine & "UNION" & vbNewLine) End If For Each par In cmd.Parameters If par.Value Is Nothing OrElse IsDBNull(par.Value) Then query = RegularExpressions.Regex.Replace(query, par.ParameterName & "\b", "NULL") ElseIf TypeOf par.Value Is Date Then query = RegularExpressions.Regex.Replace(query, par.ParameterName & "\b", "'" & Format(par.Value, "yyyy-MM-dd HH:mm:ss") & "'") ElseIf TypeOf par.Value Is TimeSpan Then query = RegularExpressions.Regex.Replace(query, par.ParameterName & "\b", "'" & par.Value.ToString & "'") ElseIf TypeOf par.Value Is Double Or TypeOf par.Value Is Decimal Or TypeOf par.Value Is Single Then query = RegularExpressions.Regex.Replace(query, par.ParameterName & "\b", Replace(par.Value.ToString, ",", ".")) ElseIf TypeOf par.Value Is Integer Or TypeOf par.Value Is UInteger Or TypeOf par.Value Is Long Or TypeOf par.Value Is ULong Then query = RegularExpressions.Regex.Replace(query, par.ParameterName & "\b", par.Value.ToString) Else query = RegularExpressions.Regex.Replace(query, par.ParameterName & "\b", "'" & MySqlHelper.EscapeString(CStr(par.Value)) & "'") End If Next 

Ejemplo:

 SELECT * FROM order WHERE order_status = @order_status AND order_date = @order_date 

Se generará:

 SELECT * FROM order WHERE order_status = 'C' AND order_date = '2015-01-01 00:00:00' 

las consultas del comando sql se ejecutarán con exec sp_executesql, así que aquí hay otra forma de obtener la instrucción como una cadena (método de extensión SqlCommand):

 public static string ToSqlStatement(this SqlCommand cmd) { return $@"EXECUTE sp_executesql N'{cmd.CommandText.Replace("'", "''")}'{cmd.Parameters.ToSqlParameters()}"; } private static string ToSqlParameters(this SqlParameterCollection col) { if (col.Count == 0) return string.Empty; var parameters = new List(); var parameterValues = new List(); foreach (SqlParameter param in col) { parameters.Add($"{param.ParameterName}{param.ToSqlParameterType()}"); parameterValues.Add($"{param.ParameterName} = {param.ToSqlParameterValue()}"); } return $",N\'{string.Join(",", parameters)}\',{string.Join(",", parameterValues)}"; } private static object ToSqlParameterType(this SqlParameter param) { var paramDbType = param.SqlDbType.ToString().ToLower(); if (param.Precision != 0 && param.Scale != 0) return $"{paramDbType}({param.Precision},{param.Scale})"; if (param.Precision != 0) return $"{paramDbType}({param.Precision})"; switch (param.SqlDbType) { case SqlDbType.VarChar: case SqlDbType.NVarChar: string s = param.SqlValue?.ToString() ?? string.Empty; return paramDbType + (s.Length > 0 ? $"({s.Length})" : string.Empty); default: return paramDbType; } } private static string ToSqlParameterValue(this SqlParameter param) { switch (param.SqlDbType) { case SqlDbType.Char: case SqlDbType.Date: case SqlDbType.DateTime: case SqlDbType.DateTime2: case SqlDbType.DateTimeOffset: case SqlDbType.NChar: case SqlDbType.NText: case SqlDbType.NVarChar: case SqlDbType.Text: case SqlDbType.Time: case SqlDbType.VarChar: case SqlDbType.Xml: return $"\'{param.SqlValue.ToString().Replace("'", "''")}\'"; case SqlDbType.Bit: return param.SqlValue.ToBooleanOrDefault() ? "1" : "0"; default: return param.SqlValue.ToString().Replace("'", "''"); } } public static bool ToBooleanOrDefault(this object o, bool defaultValue = false) { if (o == null) return defaultValue; string value = o.ToString().ToLower(); switch (value) { case "yes": case "true": case "ok": case "y": return true; case "no": case "false": case "n": return false; default: bool b; if (bool.TryParse(o.ToString(), out b)) return b; break; } return defaultValue; } 

Un trazador de líneas:

 string.Join(",", from SqlParameter p in cmd.Parameters select p.ToString()) 

Escribí este método para mí. Uso alguna parte del código de Bruno Ratnieks . Tal vez sea útil para alguien.

  public static string getQueryFromCommand(SqlCommand cmd) { StringBuilder CommandTxt = new StringBuilder(); CommandTxt.Append("DECLARE "); List paramlst = new List(); foreach (SqlParameter parms in cmd.Parameters) { paramlst.Add(parms.ParameterName); CommandTxt.Append(parms.ParameterName + " AS "); CommandTxt.Append(parms.SqlDbType.ToString()); CommandTxt.Append(","); } if (CommandTxt.ToString().Substring(CommandTxt.Length-1, 1) == ",") CommandTxt.Remove(CommandTxt.Length-1, 1); CommandTxt.AppendLine(); int rownr = 0; foreach (SqlParameter parms in cmd.Parameters) { string val = String.Empty; if (parms.DbType.Equals(DbType.String) || parms.DbType.Equals(DbType.DateTime)) val = "'" + Convert.ToString(parms.Value).Replace(@"\", @"\\").Replace("'", @"\'") + "'"; if (parms.DbType.Equals(DbType.Int16) || parms.DbType.Equals(DbType.Int32) || parms.DbType.Equals(DbType.Int64) || parms.DbType.Equals(DbType.Decimal) || parms.DbType.Equals(DbType.Double)) val = Convert.ToString(parms.Value); CommandTxt.AppendLine(); CommandTxt.Append("SET " + paramlst[rownr].ToString() + " = " + val.ToString()); rownr += 1; } CommandTxt.AppendLine(); CommandTxt.AppendLine(); CommandTxt.Append(cmd.CommandText); return CommandTxt.ToString(); }