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 diff --git a/Readme.md b/Readme.md index 9a1fd892..3070a8e9 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 parameter prefix +------- +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 +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 ------- 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..dfdf0e53 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,35 @@ 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 +836,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 +890,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 +947,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 +1003,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 +1080,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 +1134,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 +1192,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); } } 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 {