From 3fe80f8a55c81ac3ce0ea3b14b8a0284e3ee56ba Mon Sep 17 00:00:00 2001
From: bmasephol <12200343+bmasephol@users.noreply.github.com>
Date: Sun, 10 Mar 2024 21:21:55 -0500
Subject: [PATCH 1/7] Adding support for Microsoft.Data.SqlClient
---
CommandAsSql/CommandAsSql.csproj | 7 +-
CommandAsSql/ExtensionMethods.cs | 14 +-
CommandAsSql/ExtensionMethodsMicrosoft.cs | 256 ++++++++++++++++++
XUnitTestCommandAsSql/UnitTest1.cs | 10 +-
.../XUnitTestCommandAsSql.csproj | 12 +-
5 files changed, 279 insertions(+), 20 deletions(-)
create mode 100644 CommandAsSql/ExtensionMethodsMicrosoft.cs
diff --git a/CommandAsSql/CommandAsSql.csproj b/CommandAsSql/CommandAsSql.csproj
index 303c64a..8637e63 100644
--- a/CommandAsSql/CommandAsSql.csproj
+++ b/CommandAsSql/CommandAsSql.csproj
@@ -26,11 +26,12 @@
-
+
+
-
-
+
+
diff --git a/CommandAsSql/ExtensionMethods.cs b/CommandAsSql/ExtensionMethods.cs
index 84c2ddf..4495e8f 100644
--- a/CommandAsSql/ExtensionMethods.cs
+++ b/CommandAsSql/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/CommandAsSql/ExtensionMethodsMicrosoft.cs b/CommandAsSql/ExtensionMethodsMicrosoft.cs
new file mode 100644
index 0000000..c7c4443
--- /dev/null
+++ b/CommandAsSql/ExtensionMethodsMicrosoft.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/XUnitTestCommandAsSql/UnitTest1.cs b/XUnitTestCommandAsSql/UnitTest1.cs
index 49cdb53..419a8a4 100644
--- a/XUnitTestCommandAsSql/UnitTest1.cs
+++ b/XUnitTestCommandAsSql/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
{
- using System;
- using System.Data.SqlClient;
- using CommandAsSql;
- using Xunit;
-
public class UnitTest1
{
///
diff --git a/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj b/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj
index e81ac0a..7d8593d 100644
--- a/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj
+++ b/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj
@@ -7,12 +7,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers
From fbb6cbbf8437b0227be82222ef2c4b6abaedfbaa Mon Sep 17 00:00:00 2001
From: bmasephol <12200343+bmasephol@users.noreply.github.com>
Date: Sun, 10 Mar 2024 21:24:35 -0500
Subject: [PATCH 2/7] Rearranging source files.
---
.../ExtensionMethods.cs} | 0
CommandAsSql/{ => System}/ExtensionMethods.cs | 0
2 files changed, 0 insertions(+), 0 deletions(-)
rename CommandAsSql/{ExtensionMethodsMicrosoft.cs => Microsoft/ExtensionMethods.cs} (100%)
rename CommandAsSql/{ => System}/ExtensionMethods.cs (100%)
diff --git a/CommandAsSql/ExtensionMethodsMicrosoft.cs b/CommandAsSql/Microsoft/ExtensionMethods.cs
similarity index 100%
rename from CommandAsSql/ExtensionMethodsMicrosoft.cs
rename to CommandAsSql/Microsoft/ExtensionMethods.cs
diff --git a/CommandAsSql/ExtensionMethods.cs b/CommandAsSql/System/ExtensionMethods.cs
similarity index 100%
rename from CommandAsSql/ExtensionMethods.cs
rename to CommandAsSql/System/ExtensionMethods.cs
From a2bf4641282c1bcd829d72093b7ef850954c94ac Mon Sep 17 00:00:00 2001
From: bmasephol <12200343+bmasephol@users.noreply.github.com>
Date: Sun, 10 Mar 2024 21:31:48 -0500
Subject: [PATCH 3/7] Rearranging unit tests and adding test for
Microsoft.Data.SqlClient.
---
XUnitTestCommandAsSql/Microsoft/UnitTest1.cs | 48 +++++++++++++++++++
.../{ => System}/UnitTest1.cs | 2 +-
2 files changed, 49 insertions(+), 1 deletion(-)
create mode 100644 XUnitTestCommandAsSql/Microsoft/UnitTest1.cs
rename XUnitTestCommandAsSql/{ => System}/UnitTest1.cs (97%)
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 97%
rename from XUnitTestCommandAsSql/UnitTest1.cs
rename to XUnitTestCommandAsSql/System/UnitTest1.cs
index 419a8a4..50d5806 100644
--- a/XUnitTestCommandAsSql/UnitTest1.cs
+++ b/XUnitTestCommandAsSql/System/UnitTest1.cs
@@ -6,7 +6,7 @@
///
/// Unit test for the CommandAsSql lib
///
-namespace XUnitTestCommandAsSql
+namespace XUnitTestCommandAsSql.System
{
public class UnitTest1
{
From 830df9a72a5db644426aa1b892ece81725f652c1 Mon Sep 17 00:00:00 2001
From: bmasephol <12200343+bmasephol@users.noreply.github.com>
Date: Mon, 18 Mar 2024 14:34:40 -0500
Subject: [PATCH 4/7] Reverting package version to latest stable.
---
XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj b/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj
index 7d8593d..7270590 100644
--- a/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj
+++ b/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj
@@ -7,7 +7,7 @@
-
+
From 8d7f009aeb6893712fc701e7b353ea77da9fa5fa Mon Sep 17 00:00:00 2001
From: bmasephol <12200343+bmasephol@users.noreply.github.com>
Date: Mon, 18 Mar 2024 15:32:07 -0500
Subject: [PATCH 5/7] Updating release notes in preparation for a version 1.1.0
release.
---
CommandAsSql/CommandAsSql.csproj | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/CommandAsSql/CommandAsSql.csproj b/CommandAsSql/CommandAsSql.csproj
index 8637e63..0dabff0 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.3
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
From a6587ae714fbc0b1f5cbc2cdd2636118d700dc48 Mon Sep 17 00:00:00 2001
From: bmasephol <12200343+bmasephol@users.noreply.github.com>
Date: Wed, 16 Oct 2024 21:29:16 -0500
Subject: [PATCH 6/7] Updated package versions to latest.
---
CommandAsSql/CommandAsSql.csproj | 2 +-
XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/CommandAsSql/CommandAsSql.csproj b/CommandAsSql/CommandAsSql.csproj
index 0dabff0..bdc18f5 100644
--- a/CommandAsSql/CommandAsSql.csproj
+++ b/CommandAsSql/CommandAsSql.csproj
@@ -25,7 +25,7 @@
-
+
diff --git a/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj b/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj
index 7270590..f9130dc 100644
--- a/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj
+++ b/XUnitTestCommandAsSql/XUnitTestCommandAsSql.csproj
@@ -7,12 +7,12 @@
-
-
-
+
+
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers
From b1e5aa0b08a29090bb4e8b924c1fd429e6c9bc28 Mon Sep 17 00:00:00 2001
From: bmasephol <12200343+bmasephol@users.noreply.github.com>
Date: Wed, 16 Oct 2024 21:38:34 -0500
Subject: [PATCH 7/7] Bumping package version.
---
CommandAsSql/CommandAsSql.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CommandAsSql/CommandAsSql.csproj b/CommandAsSql/CommandAsSql.csproj
index bdc18f5..d613857 100644
--- a/CommandAsSql/CommandAsSql.csproj
+++ b/CommandAsSql/CommandAsSql.csproj
@@ -10,7 +10,7 @@
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.1.0-alpha.3
+ 1.1.0-alpha.4
MIT
true
1.1.0 - Added support for Microsoft.Data.SqlClient