diff --git a/CommandAsSql/CommandAsSql.csproj b/CommandAsSql/CommandAsSql.csproj
index c468139..d613857 100644
--- a/CommandAsSql/CommandAsSql.csproj
+++ b/CommandAsSql/CommandAsSql.csproj
@@ -10,15 +10,14 @@
SqlCommand String ToString SqlClient
Displays a SqlCommand as a full SQL string with all parameters inline. Makes it easy to debug (copy paste to db tool). Please submit pull requests on GitHub.
jphellemons, flapper
- 1.0.10
+ 1.1.0-alpha.4
MIT
true
-
- 1.0.10 included icon and changed ref to license and added readme.md to package (for nuget.org)
- 1.0.9 added readme.md in package
- 1.0.8 changed github action and upgraded sqlclient dependency
- 1.0.3 Added the MIT license to both the nuget and the github repo
-
+ 1.1.0 - Added support for Microsoft.Data.SqlClient
+1.0.10 - Included icon and changed ref to license and added readme.md to package (for nuget.org)
+1.0.9 - Added readme.md in package
+1.0.8 - Changed github action and upgraded sqlclient dependency
+1.0.3 - Added the MIT license to both the nuget and the github repo
@@ -26,11 +25,12 @@
+
-
-
+
+
diff --git a/CommandAsSql/Microsoft/ExtensionMethods.cs b/CommandAsSql/Microsoft/ExtensionMethods.cs
new file mode 100644
index 0000000..c7c4443
--- /dev/null
+++ b/CommandAsSql/Microsoft/ExtensionMethods.cs
@@ -0,0 +1,256 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using Microsoft.Data.SqlClient;
+using System.Text.RegularExpressions;
+using System.Text;
+using System.Globalization;
+
+namespace CommandAsSql.Microsoft
+{
+ ///
+ /// Extension method to parse a SqlCommand as string with filled SqlParameters. Makes it easy to paste the string in a Database Management tool to debug and profile etc.
+ ///
+ public static class ExtensionMethods
+ {
+ #region Boolean Helpers
+ ///
+ /// extension method overload for a string
+ ///
+ ///
+ ///
+ ///
+ public static bool ToBooleanOrDefault(this string s, bool defaultValue)
+ {
+ return ToBooleanOrDefault((object)s, defaultValue);
+ }
+
+ ///
+ /// extension method to get a bool value from an object
+ ///
+ ///
+ ///
+ ///
+ public static bool ToBooleanOrDefault(this object o, bool defaultValue)
+ {
+ bool result = defaultValue;
+
+ if (o != null)
+ {
+ try
+ {
+ switch (o.ToString().ToLower())
+ {
+ case "yes":
+ case "true":
+ case "ok":
+ case "y":
+ result = true;
+ break;
+
+ case "no":
+ case "false":
+ case "n":
+ result = false;
+ break;
+
+ default:
+ result = bool.Parse(o.ToString());
+ break;
+ }
+ }
+ catch
+ {
+ }
+ }
+
+ return result;
+ }
+
+ #endregion
+
+ #region SQL Helpers
+
+ ///
+ /// Turns a parameter object to string
+ ///
+ /// SqlParameter
+ ///
+ public static string ParameterValueForSQL(this SqlParameter param)
+ {
+ object paramValue = param.Value; //assuming param isn't null
+
+ if (paramValue == null) //TODO: should probably use DBNull.Value instead or in combination with this
+ return "NULL"; //TODO: naive code, won't work as is, need to replace later on = NULL with IS NULL at non-Update queries
+
+ switch (param.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:
+ return $"'{paramValue.ToString().Replace("'", "''")}'";
+
+ case SqlDbType.Bit:
+ return (paramValue.ToBooleanOrDefault(false)) ? "1" : "0";
+
+ case SqlDbType.Structured:
+ var sb = new StringBuilder();
+ var dt = (DataTable)paramValue;
+
+ sb.Append("declare ").Append(param.ParameterName).Append(" ").AppendLine(param.TypeName);
+
+ foreach (DataRow dr in dt.Rows)
+ {
+ sb.Append("insert ").Append(param.ParameterName).Append(" values (");
+
+ for (int colIndex = 0; colIndex < dt.Columns.Count; colIndex++)
+ {
+ switch (Type.GetTypeCode(dr[colIndex].GetType()))
+ {
+ case TypeCode.Boolean:
+ sb.Append(Convert.ToInt32(dr[colIndex]));
+ break;
+
+ case TypeCode.String:
+ sb.Append("'").Append(dr[colIndex]).Append("'");
+ break;
+
+ case TypeCode.DateTime:
+ sb.Append("'").Append(Convert.ToDateTime(dr[colIndex]).ToString("yyyy-MM-dd HH:mm")).Append("'");
+ break;
+
+ default:
+ sb.Append(dr[colIndex]); break;
+ }
+
+ sb.Append(", ");
+ }
+
+ sb.Length -= 2; // trailing ', '
+ sb.AppendLine(")");
+ }
+
+ return sb.ToString();
+
+ case SqlDbType.Decimal:
+ case SqlDbType.Float:
+ return ((double)paramValue).ToString(CultureInfo.InvariantCulture).Replace("'", "''");
+
+ default:
+ return paramValue.ToString().Replace("'", "''");
+ }
+ }
+
+ private static List GetStructured(this SqlParameterCollection paramCollection)
+ {
+ List filtered = new List();
+ foreach (SqlParameter p in paramCollection)
+ {
+ if (p.SqlDbType == SqlDbType.Structured)
+ filtered.Add(p);
+ }
+
+ return filtered;
+ }
+
+ #endregion
+
+ ///
+ /// This method fills all parameters of the sqlcommand and displays it as a string which can be copy pasted in your DB management tool for debug purposes.
+ ///
+ /// The SqlCommand you want parsed as a full SQL string
+ ///
+ public static string CommandAsSql(this SqlCommand command)
+ {
+ var sql = new StringBuilder();
+
+ if (command.Connection != null)
+ sql.Append("use ").Append(command.Connection.Database).AppendLine(";");
+
+ foreach (SqlParameter strucParam in command.Parameters.GetStructured())
+ sql.AppendLine(strucParam.ParameterValueForSQL());
+
+ switch (command.CommandType)
+ {
+ case CommandType.Text: //checking 1st, since if we use Text SQL Commands we'll probably be logging more of them that if we had them grouped in Stored Procedures
+ command.CommandAsSql_Text(sql);
+ break;
+
+ case CommandType.StoredProcedure:
+ command.CommandAsSql_StoredProcedure(sql);
+ break;
+ }
+
+ return sql.ToString();
+ }
+
+ private static void CommandAsSql_Text(this SqlCommand command, StringBuilder sql)
+ {
+ string query = command.CommandText;
+
+ foreach (SqlParameter p in command.Parameters)
+ query = Regex.Replace(query, "\\B" + p.ParameterName + "\\b", p.ParameterValueForSQL()); //the first one is \B, the 2nd one is \b, since ParameterName starts with @ which is a non-word character in RegEx (see https://stackoverflow.com/a/2544661)
+
+ sql.AppendLine(query);
+ }
+
+ private static void CommandAsSql_StoredProcedure(this SqlCommand command, StringBuilder sql)
+ {
+ sql.AppendLine("declare @return_value int;");
+
+ foreach (SqlParameter sp in command.Parameters)
+ {
+ if ((sp.Direction == ParameterDirection.InputOutput) || (sp.Direction == ParameterDirection.Output))
+ {
+ sql.Append("declare ").Append(sp.ParameterName).Append("\t").Append(sp.SqlDbType.ToString()).Append("\t= ");
+
+ sql.Append((sp.Direction == ParameterDirection.Output) ? "null" : sp.ParameterValueForSQL()).AppendLine(";");
+ }
+ }
+
+ sql.Append("exec [").Append(command.CommandText).AppendLine("]");
+
+ bool FirstParam = true;
+ foreach (SqlParameter param in command.Parameters)
+ {
+ if (param.Direction != ParameterDirection.ReturnValue)
+ {
+ sql.Append((FirstParam) ? "\t" : "\t, ");
+
+ if (FirstParam)
+ FirstParam = false;
+
+ if (param.Direction == ParameterDirection.Input)
+ {
+ if (param.SqlDbType != SqlDbType.Structured)
+ sql.Append(param.ParameterName).Append(" = ").AppendLine(param.ParameterValueForSQL());
+ else
+ sql.Append(param.ParameterName).Append(" = ").AppendLine(param.ParameterName);
+ }
+ else
+ {
+ sql.Append(param.ParameterName).Append(" = ").Append(param.ParameterName).AppendLine(" output");
+ }
+ }
+ }
+ sql.AppendLine(";");
+
+ sql.AppendLine("select 'Return Value' = convert(varchar, @return_value);");
+
+ foreach (SqlParameter sp in command.Parameters)
+ {
+ if ((sp.Direction == ParameterDirection.InputOutput) || (sp.Direction == ParameterDirection.Output))
+ sql.Append("select '").Append(sp.ParameterName).Append("' = convert(varchar, ").Append(sp.ParameterName).AppendLine(");");
+ }
+ }
+ }
+}
diff --git a/CommandAsSql/ExtensionMethods.cs b/CommandAsSql/System/ExtensionMethods.cs
similarity index 96%
rename from CommandAsSql/ExtensionMethods.cs
rename to CommandAsSql/System/ExtensionMethods.cs
index 84c2ddf..4495e8f 100644
--- a/CommandAsSql/ExtensionMethods.cs
+++ b/CommandAsSql/System/ExtensionMethods.cs
@@ -2,9 +2,11 @@
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
+using System.Globalization;
+using System.Text;
using System.Text.RegularExpressions;
-namespace CommandAsSql
+namespace CommandAsSql.System
{
///
/// Extension method to parse a SqlCommand as string with filled SqlParameters. Makes it easy to paste the string in a Database Management tool to debug and profile etc.
@@ -101,7 +103,7 @@ public static string ParameterValueForSQL(this SqlParameter param)
return (paramValue.ToBooleanOrDefault(false)) ? "1" : "0";
case SqlDbType.Structured:
- var sb = new System.Text.StringBuilder();
+ var sb = new StringBuilder();
var dt = (DataTable)paramValue;
sb.Append("declare ").Append(param.ParameterName).Append(" ").AppendLine(param.TypeName);
@@ -141,7 +143,7 @@ public static string ParameterValueForSQL(this SqlParameter param)
case SqlDbType.Decimal:
case SqlDbType.Float:
- return ((double)paramValue).ToString(System.Globalization.CultureInfo.InvariantCulture).Replace("'", "''");
+ return ((double)paramValue).ToString(CultureInfo.InvariantCulture).Replace("'", "''");
default:
return paramValue.ToString().Replace("'", "''");
@@ -169,7 +171,7 @@ private static List GetStructured(this SqlParameterCollection para
///
public static string CommandAsSql(this SqlCommand command)
{
- var sql = new System.Text.StringBuilder();
+ var sql = new StringBuilder();
if (command.Connection != null)
sql.Append("use ").Append(command.Connection.Database).AppendLine(";");
@@ -191,7 +193,7 @@ public static string CommandAsSql(this SqlCommand command)
return sql.ToString();
}
- private static void CommandAsSql_Text(this SqlCommand command, System.Text.StringBuilder sql)
+ private static void CommandAsSql_Text(this SqlCommand command, StringBuilder sql)
{
string query = command.CommandText;
@@ -201,7 +203,7 @@ private static void CommandAsSql_Text(this SqlCommand command, System.Text.Strin
sql.AppendLine(query);
}
- private static void CommandAsSql_StoredProcedure(this SqlCommand command, System.Text.StringBuilder sql)
+ private static void CommandAsSql_StoredProcedure(this SqlCommand command, StringBuilder sql)
{
sql.AppendLine("declare @return_value int;");
diff --git a/XUnitTestCommandAsSql/Microsoft/UnitTest1.cs b/XUnitTestCommandAsSql/Microsoft/UnitTest1.cs
new file mode 100644
index 0000000..b65136c
--- /dev/null
+++ b/XUnitTestCommandAsSql/Microsoft/UnitTest1.cs
@@ -0,0 +1,48 @@
+using System;
+using Microsoft.Data.SqlClient;
+using CommandAsSql.Microsoft;
+using Xunit;
+
+///
+/// Unit test for the CommandAsSql lib
+///
+namespace XUnitTestCommandAsSql.Microsoft
+{
+ public class UnitTest1
+ {
+ ///
+ /// Test a regular sql string with an int parameter
+ ///
+ [Fact(DisplayName = "Compare Command With Int Parameter")]
+ public void Compare_Command_With_Int_Parameter()
+ {
+ // arrange
+ const string Expected = "select * from products where productid = 1";
+ var sc = new SqlCommand("select * from products where productid = @id");
+ sc.Parameters.AddWithValue("@id", 1); // if you forget the @ here, the test fails. Give your thoughts about this in the GitHub issues section please.
+
+ // act
+ var c = sc.CommandAsSql().Replace(Environment.NewLine, string.Empty); // not too happy about removing the newline
+
+ // assert
+ Assert.Equal(Expected, c);
+ }
+
+ [Fact(DisplayName = "Compare Command With String Parameter")]
+ public void Compare_Command_With_String_Parameter()
+ {
+ // arrange
+ const string Expected = "select * from products where productname = 'myname'";
+ var sc = new SqlCommand("select * from products where productname = @name");
+ sc.Parameters.AddWithValue("@name", "myname"); // if you forget the @ here, the test fails. Give your thoughts about this in the GitHub issues section please.
+
+ // act
+ var c = sc.CommandAsSql().Replace(Environment.NewLine, string.Empty); // not too happy about removing the newline
+
+ // assert
+ Assert.Equal(Expected, c);
+ }
+
+ // todo: add more tests for structured, stored proc etc.
+ }
+}
diff --git a/XUnitTestCommandAsSql/UnitTest1.cs b/XUnitTestCommandAsSql/System/UnitTest1.cs
similarity index 93%
rename from XUnitTestCommandAsSql/UnitTest1.cs
rename to XUnitTestCommandAsSql/System/UnitTest1.cs
index 49cdb53..50d5806 100644
--- a/XUnitTestCommandAsSql/UnitTest1.cs
+++ b/XUnitTestCommandAsSql/System/UnitTest1.cs
@@ -1,13 +1,13 @@
+using System;
+using System.Data.SqlClient;
+using CommandAsSql.System;
+using Xunit;
+
///
/// Unit test for the CommandAsSql lib
///
-namespace XUnitTestCommandAsSql
+namespace XUnitTestCommandAsSql.System
{
- using System;
- using System.Data.SqlClient;
- using CommandAsSql;
- using Xunit;
-
public class UnitTest1
{
///
diff --git a/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj b/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj
index 0682aaa..f9130dc 100644
--- a/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj
+++ b/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj
@@ -7,12 +7,12 @@
-
-
-
+
+
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers