From 6765ff2a2f3a4636a13abdd921aa79855062bc30 Mon Sep 17 00:00:00 2001 From: Jonatas Cruz Date: Wed, 28 Dec 2022 22:47:45 -0300 Subject: [PATCH 1/7] fix: fixing project path on solution file. --- Dapper.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dapper.sln b/Dapper.sln index e993c7a4..4aa75f10 100644 --- a/Dapper.sln +++ b/Dapper.sln @@ -16,7 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution version.json = version.json EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Contrib", "Dapper.Contrib\Dapper.Contrib.csproj", "{4E409F8F-CFBB-4332-8B0A-FD5A283051FD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Contrib", "src\Dapper.Contrib\Dapper.Contrib.csproj", "{4E409F8F-CFBB-4332-8B0A-FD5A283051FD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Tests.Contrib", "tests\Dapper.Tests.Contrib\Dapper.Tests.Contrib.csproj", "{DAB3C5B7-BCD1-4A5F-BB6B-50D2BB63DB4A}" EndProject From 12996625927443de767071f9abe2481d2cd81592 Mon Sep 17 00:00:00 2001 From: Jonatas Cruz Date: Wed, 28 Dec 2022 22:51:33 -0300 Subject: [PATCH 2/7] feat: adding support to define a custom parameter prefix, for both SQL query and parameter collection. --- .../SqlMapperExtensions.Async.cs | 15 ++-- src/Dapper.Contrib/SqlMapperExtensions.cs | 80 ++++++++++++++----- 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/src/Dapper.Contrib/SqlMapperExtensions.Async.cs b/src/Dapper.Contrib/SqlMapperExtensions.Async.cs index c93e39a4..adfb134f 100644 --- a/src/Dapper.Contrib/SqlMapperExtensions.Async.cs +++ b/src/Dapper.Contrib/SqlMapperExtensions.Async.cs @@ -30,12 +30,12 @@ public static async Task GetAsync(this IDbConnection connection, dynamic i var key = GetSingleKey(nameof(GetAsync)); var name = GetTableName(type); - sql = $"SELECT * FROM {name} WHERE {key.Name} = @id"; + sql = $"SELECT * FROM {name} WHERE {key.Name} = {GetParameterPrefixQuery()}id"; GetQueries[type.TypeHandle] = sql; } var dynParams = new DynamicParameters(); - dynParams.Add("@id", id); + dynParams.Add($"{GetParameterPrefixParams()}id", id); if (!type.IsInterface) return (await connection.QueryAsync(sql, dynParams, transaction, commandTimeout).ConfigureAwait(false)).FirstOrDefault(); @@ -168,6 +168,7 @@ public static Task InsertAsync(this IDbConnection connection, T entityTo var keyProperties = KeyPropertiesCache(type).ToList(); var computedProperties = ComputedPropertiesCache(type); var allPropertiesExceptKeyAndComputed = allProperties.Except(keyProperties.Union(computedProperties)).ToList(); + string parameterPrefix = GetParameterPrefixQuery(); for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++) { @@ -181,7 +182,7 @@ public static Task InsertAsync(this IDbConnection connection, T entityTo for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++) { var property = allPropertiesExceptKeyAndComputed[i]; - sbParameterList.AppendFormat("@{0}", property.Name); + sbParameterList.AppendFormat("{0}{1}", parameterPrefix, property.Name); if (i < allPropertiesExceptKeyAndComputed.Count - 1) sbParameterList.Append(", "); } @@ -248,11 +249,12 @@ public static async Task UpdateAsync(this IDbConnection connection, T e var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties)).ToList(); var adapter = GetFormatter(connection); + string parameterPrefix = GetParameterPrefixQuery(); for (var i = 0; i < nonIdProps.Count; i++) { var property = nonIdProps[i]; - adapter.AppendColumnNameEqualsValue(sb, property.Name); + adapter.AppendColumnNameEqualsValue(sb, property.Name, parameterPrefix); if (i < nonIdProps.Count - 1) sb.Append(", "); } @@ -260,7 +262,7 @@ public static async Task UpdateAsync(this IDbConnection connection, T e for (var i = 0; i < keyProperties.Count; i++) { var property = keyProperties[i]; - adapter.AppendColumnNameEqualsValue(sb, property.Name); + adapter.AppendColumnNameEqualsValue(sb, property.Name, parameterPrefix); if (i < keyProperties.Count - 1) sb.Append(" and "); } @@ -313,11 +315,12 @@ public static async Task DeleteAsync(this IDbConnection connection, T e sb.AppendFormat("DELETE FROM {0} WHERE ", name); var adapter = GetFormatter(connection); + string parameterPrefix = GetParameterPrefixQuery(); for (var i = 0; i < allKeyProperties.Count; i++) { var property = allKeyProperties[i]; - adapter.AppendColumnNameEqualsValue(sb, property.Name); + adapter.AppendColumnNameEqualsValue(sb, property.Name, parameterPrefix); if (i < allKeyProperties.Count - 1) sb.Append(" AND "); } diff --git a/src/Dapper.Contrib/SqlMapperExtensions.cs b/src/Dapper.Contrib/SqlMapperExtensions.cs index 9a30e805..8d0f41c1 100644 --- a/src/Dapper.Contrib/SqlMapperExtensions.cs +++ b/src/Dapper.Contrib/SqlMapperExtensions.cs @@ -41,6 +41,12 @@ public interface ITableNameMapper string GetTableName(Type type); } + /// + /// Defines a custom parameter prefix. + /// + /// + public delegate string GetParameterPrefixDelegate(); + /// /// The function to get a database type from the given . /// @@ -176,12 +182,12 @@ public static T Get(this IDbConnection connection, dynamic id, IDbTransaction var key = GetSingleKey(nameof(Get)); var name = GetTableName(type); - sql = $"select * from {name} where {key.Name} = @id"; + sql = $"select * from {name} where {key.Name} = {GetParameterPrefixQuery()}id"; GetQueries[type.TypeHandle] = sql; } var dynParams = new DynamicParameters(); - dynParams.Add("@id", id); + dynParams.Add($"{GetParameterPrefixParams()}id", id); T obj; @@ -350,6 +356,7 @@ public static long Insert(this IDbConnection connection, T entityToInsert, ID var allPropertiesExceptKeyAndComputed = allProperties.Except(keyProperties.Union(computedProperties)).ToList(); var adapter = GetFormatter(connection); + var parameterPrefix = GetParameterPrefixQuery(); for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++) { @@ -363,7 +370,7 @@ public static long Insert(this IDbConnection connection, T entityToInsert, ID for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++) { var property = allPropertiesExceptKeyAndComputed[i]; - sbParameterList.AppendFormat("@{0}", property.Name); + sbParameterList.AppendFormat("{0}{1}", parameterPrefix, property.Name); if (i < allPropertiesExceptKeyAndComputed.Count - 1) sbParameterList.Append(", "); } @@ -438,11 +445,12 @@ public static bool Update(this IDbConnection connection, T entityToUpdate, ID var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties)).ToList(); var adapter = GetFormatter(connection); + string parameterPrefix = GetParameterPrefixQuery(); for (var i = 0; i < nonIdProps.Count; i++) { var property = nonIdProps[i]; - adapter.AppendColumnNameEqualsValue(sb, property.Name); //fix for issue #336 + adapter.AppendColumnNameEqualsValue(sb, property.Name, parameterPrefix); //fix for issue #336 if (i < nonIdProps.Count - 1) sb.Append(", "); } @@ -450,7 +458,7 @@ public static bool Update(this IDbConnection connection, T entityToUpdate, ID for (var i = 0; i < keyProperties.Count; i++) { var property = keyProperties[i]; - adapter.AppendColumnNameEqualsValue(sb, property.Name); //fix for issue #336 + adapter.AppendColumnNameEqualsValue(sb, property.Name, parameterPrefix); //fix for issue #336 if (i < keyProperties.Count - 1) sb.Append(" and "); } @@ -503,11 +511,12 @@ public static bool Delete(this IDbConnection connection, T entityToDelete, ID sb.AppendFormat("delete from {0} where ", name); var adapter = GetFormatter(connection); + string parameterPrefix = GetParameterPrefixQuery(); for (var i = 0; i < keyProperties.Count; i++) { var property = keyProperties[i]; - adapter.AppendColumnNameEqualsValue(sb, property.Name); //fix for issue #336 + adapter.AppendColumnNameEqualsValue(sb, property.Name, parameterPrefix); //fix for issue #336 if (i < keyProperties.Count - 1) sb.Append(" and "); } @@ -532,6 +541,32 @@ public static bool DeleteAll(this IDbConnection connection, IDbTransaction tr return deleted > 0; } + /// + /// Defines a custom parameter prefix in the query. will be used as default + /// +#pragma warning disable CA2211 // Non-constant fields should not be visible - I agree with you, but we can't do that until we break the API + public static GetParameterPrefixDelegate GetParameterPrefixForQuery; +#pragma warning restore CA2211 // Non-constant fields should not be visible + + /// + /// Defines a custom parameter prefix in the parameters collection. will be used as default + /// +#pragma warning disable CA2211 // Non-constant fields should not be visible - I agree with you, but we can't do that until we break the API + public static GetParameterPrefixDelegate GetParameterPrefixForParameterCollection; +#pragma warning restore CA2211 // Non-constant fields should not be visible + + private const string _defaultParameterPrefix = "@"; + + private static string GetParameterPrefixQuery() + { + return GetParameterPrefixForQuery?.Invoke() ?? _defaultParameterPrefix; + } + + private static string GetParameterPrefixParams() + { + return GetParameterPrefixForParameterCollection?.Invoke() ?? _defaultParameterPrefix; + } + /// /// Specifies a custom callback that detects the database type instead of relying on the default strategy (the name of the connection type object). /// Please note that this callback is global and will be used by all the calls that require a database specific adapter. @@ -798,7 +833,8 @@ public partial interface ISqlAdapter /// /// The string builder to append to. /// The column name. - void AppendColumnNameEqualsValue(StringBuilder sb, string columnName); + /// Parameter prefix. + void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string parameterPrefix); } /// @@ -851,9 +887,10 @@ public void AppendColumnName(StringBuilder sb, string columnName) /// /// The string builder to append to. /// The column name. - public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + /// Parameter prefix. + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string parameterPrefix) { - sb.AppendFormat("[{0}] = @{1}", columnName, columnName); + sb.AppendFormat("[{0}] = {2}{1}", columnName, columnName, parameterPrefix); } } @@ -907,9 +944,10 @@ public void AppendColumnName(StringBuilder sb, string columnName) /// /// The string builder to append to. /// The column name. - public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + /// Parameter prefix. + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string parameterPrefix) { - sb.AppendFormat("[{0}] = @{1}", columnName, columnName); + sb.AppendFormat("[{0}] = {2}{1}", columnName, columnName, parameterPrefix); } } @@ -962,9 +1000,10 @@ public void AppendColumnName(StringBuilder sb, string columnName) /// /// The string builder to append to. /// The column name. - public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + /// Parameter prefix. + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string parameterPrefix) { - sb.AppendFormat("`{0}` = @{1}", columnName, columnName); + sb.AppendFormat("`{0}` = {2}{1}", columnName, columnName, parameterPrefix); } } @@ -1038,9 +1077,10 @@ public void AppendColumnName(StringBuilder sb, string columnName) /// /// The string builder to append to. /// The column name. - public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + /// Parameter prefix. + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string parameterPrefix) { - sb.AppendFormat("\"{0}\" = @{1}", columnName, columnName); + sb.AppendFormat("\"{0}\" = {2}{1}", columnName, columnName, parameterPrefix); } } @@ -1091,9 +1131,10 @@ public void AppendColumnName(StringBuilder sb, string columnName) /// /// The string builder to append to. /// The column name. - public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + /// Parameter prefix. + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string parameterPrefix) { - sb.AppendFormat("\"{0}\" = @{1}", columnName, columnName); + sb.AppendFormat("\"{0}\" = {2}{1}", columnName, columnName, parameterPrefix); } } @@ -1148,8 +1189,9 @@ public void AppendColumnName(StringBuilder sb, string columnName) /// /// The string builder to append to. /// The column name. - public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + /// Parameter prefix. + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string parameterPrefix) { - sb.AppendFormat("{0} = @{1}", columnName, columnName); + sb.AppendFormat("{0} = {2}{1}", columnName, columnName, parameterPrefix); } } From f91ab6df6a724a57e1289e1bb3f7079fe9d65467 Mon Sep 17 00:00:00 2001 From: Jonatas Cruz Date: Wed, 28 Dec 2022 22:52:20 -0300 Subject: [PATCH 3/7] test: adding unit test to define a custom parameter prefix. --- tests/Dapper.Tests.Contrib/TestSuite.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/Dapper.Tests.Contrib/TestSuite.cs b/tests/Dapper.Tests.Contrib/TestSuite.cs index 71dbd8ad..5111b2c4 100644 --- a/tests/Dapper.Tests.Contrib/TestSuite.cs +++ b/tests/Dapper.Tests.Contrib/TestSuite.cs @@ -375,6 +375,15 @@ public void InsertList() InsertHelper(src => src.ToList()); } + [Fact] + public void InsertListWithCustomParameterPrefix() + { + SqlMapperExtensions.GetParameterPrefixForQuery = () => "@"; + SqlMapperExtensions.GetParameterPrefixForParameterCollection = () => "@"; + + InsertHelper(src => src.ToList()); + } + private void InsertHelper(Func, T> helper) where T : class { From 187ded6230fe1c0af608b5b3bd4e0f580c1fe913 Mon Sep 17 00:00:00 2001 From: Jonatas Cruz Date: Wed, 28 Dec 2022 23:02:19 -0300 Subject: [PATCH 4/7] fix: changing xml documentation --- src/Dapper.Contrib/SqlMapperExtensions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Dapper.Contrib/SqlMapperExtensions.cs b/src/Dapper.Contrib/SqlMapperExtensions.cs index 8d0f41c1..dfdf0e53 100644 --- a/src/Dapper.Contrib/SqlMapperExtensions.cs +++ b/src/Dapper.Contrib/SqlMapperExtensions.cs @@ -555,6 +555,9 @@ public static bool DeleteAll(this IDbConnection connection, IDbTransaction tr public static GetParameterPrefixDelegate GetParameterPrefixForParameterCollection; #pragma warning restore CA2211 // Non-constant fields should not be visible + /// + /// '@' + /// private const string _defaultParameterPrefix = "@"; private static string GetParameterPrefixQuery() From 2f8efdd6cf8cae5f8fff2eb6bbf3d2c449c10e74 Mon Sep 17 00:00:00 2001 From: Jonatas Cruz Date: Fri, 6 Jan 2023 18:46:25 -0300 Subject: [PATCH 5/7] docs: adding documentation describing how to set a custom parameter with example. --- Readme.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Readme.md b/Readme.md index 9a1fd892..148bad18 100644 --- a/Readme.md +++ b/Readme.md @@ -167,6 +167,18 @@ Dapper.Contrib makes use of some optional attributes: * `[Write(true/false)]` - this property is (not) writeable * `[Computed]` - this property is computed and should not be part of updates +Custom prefix for parameters +------- +By default Dapper.Contrib uses the character `@` as parameter prefix for SQL query and parameters collection. This can be changed throught the `GetParameterPrefixForQuery` and `GetParameterPrefixForParameterCollection` delegates: + +```csharp +// Defines ':' as prefix for SQL query and '?' for parameters +SqlMapperExtensions.GetParameterPrefixForQuery = () => ":"; +SqlMapperExtensions.GetParameterPrefixForParameterCollection = () => "?"; +``` + +This setting may be useful when using Dapper.Contrib with database providers that use other character as a parameter prefix. + Limitations and caveats ------- From bee534287d96d8479f57350f019b2fa7c3ea7566 Mon Sep 17 00:00:00 2001 From: Jonatas Cruz Date: Fri, 6 Jan 2023 18:50:52 -0300 Subject: [PATCH 6/7] docs: fixing documentation about parameter prefix. --- Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index 148bad18..156702a1 100644 --- a/Readme.md +++ b/Readme.md @@ -167,12 +167,12 @@ Dapper.Contrib makes use of some optional attributes: * `[Write(true/false)]` - this property is (not) writeable * `[Computed]` - this property is computed and should not be part of updates -Custom prefix for parameters +Custom parameter prefix ------- By default Dapper.Contrib uses the character `@` as parameter prefix for SQL query and parameters collection. This can be changed throught the `GetParameterPrefixForQuery` and `GetParameterPrefixForParameterCollection` delegates: ```csharp -// Defines ':' as prefix for SQL query and '?' for parameters +// Defines ':' as prefix for SQL query and '?' for parameters collection SqlMapperExtensions.GetParameterPrefixForQuery = () => ":"; SqlMapperExtensions.GetParameterPrefixForParameterCollection = () => "?"; ``` From d6706f0bd62a5d019470bca0e2d1f608f5730987 Mon Sep 17 00:00:00 2001 From: Jonatas Cruz Date: Mon, 23 Jan 2023 21:53:49 -0300 Subject: [PATCH 7/7] docs: fixing typo in readme --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 156702a1..3070a8e9 100644 --- a/Readme.md +++ b/Readme.md @@ -169,7 +169,7 @@ Dapper.Contrib makes use of some optional attributes: Custom parameter prefix ------- -By default Dapper.Contrib uses the character `@` as parameter prefix for SQL query and parameters collection. This can be changed throught the `GetParameterPrefixForQuery` and `GetParameterPrefixForParameterCollection` delegates: +By default Dapper.Contrib uses the character `@` as parameter prefix for SQL query and parameters collection. This can be changed through the `GetParameterPrefixForQuery` and `GetParameterPrefixForParameterCollection` delegates: ```csharp // Defines ':' as prefix for SQL query and '?' for parameters collection