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..86e294a7 100644 --- a/Readme.md +++ b/Readme.md @@ -166,7 +166,19 @@ 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 - +* `[Column("Columnname")]` - this property has a different name in the Database + * Property is called EmployeeId but Column in DB is called employee_id + + ```csharp + public class Employee + { + [ExplicitKey] + [Column("employee_id")] + public Guid EmployeeId { get; set; } + public string Name { get; set; } + } + ``` + Limitations and caveats ------- diff --git a/src/Dapper.Contrib/PropertyInfoExtensions.cs b/src/Dapper.Contrib/PropertyInfoExtensions.cs new file mode 100644 index 00000000..7d362640 --- /dev/null +++ b/src/Dapper.Contrib/PropertyInfoExtensions.cs @@ -0,0 +1,55 @@ +using System.Collections.Concurrent; +using System.Linq; +using System.Reflection; + +namespace Dapper.Contrib.Extensions +{ + /// + /// Map to database column extensions + /// + public static class PropertyInfoExtensions + { + private static readonly ConcurrentDictionary PropertyColumnName = new ConcurrentDictionary(); + + /// + /// The function to get a column name from a given + /// + /// The to get a column name for. + public delegate string ColumnNameMapperDelegate(PropertyInfo property); + + /// + /// Specify a custom column name mapper based on the POCO type name + /// +#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 ColumnNameMapperDelegate ColumnNameMapper; +#pragma warning restore CA2211 // Non-constant fields should not be visible + + /// + /// Get database column name from + /// + /// The to get a column name for. + public static string ColumnName(this PropertyInfo property) + { + if (PropertyColumnName.TryGetValue(property, out string name)) return name; + + if (ColumnNameMapper != null) + { + name = ColumnNameMapper(property); + } + else + { + //NOTE: This as dynamic trick falls back to handle both our own Column-attribute as well as the one in EntityFramework + var columnAttrName = + property.GetCustomAttribute(true)?.Name + ?? (property.GetCustomAttributes(true).FirstOrDefault(attr => attr.GetType().Name == "ColumnAttribute") as dynamic)?.Name; + + name = columnAttrName != null + ? columnAttrName + : property.Name; + } + + PropertyColumnName[property] = name; + return name; + } + } +} diff --git a/src/Dapper.Contrib/SqlMapperExtensions.Async.cs b/src/Dapper.Contrib/SqlMapperExtensions.Async.cs index c93e39a4..f6335e03 100644 --- a/src/Dapper.Contrib/SqlMapperExtensions.Async.cs +++ b/src/Dapper.Contrib/SqlMapperExtensions.Async.cs @@ -25,13 +25,34 @@ public static partial class SqlMapperExtensions public static async Task GetAsync(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int? commandTimeout = null) where T : class { var type = typeof(T); - if (!GetQueries.TryGetValue(type.TypeHandle, out string sql)) + var properties = TypePropertiesCache(type); + var adapter = GetFormatter(connection); + + var getQueriesKey = new GetQueriesCacheKey(type.TypeHandle, adapter.GetType().TypeHandle); + + if (!GetQueries.TryGetValue(getQueriesKey, out string sql)) { var key = GetSingleKey(nameof(GetAsync)); - var name = GetTableName(type); + var name = type.TableName(); + + StringBuilder sqlStringBuilder = new StringBuilder("select "); + + foreach (PropertyInfo property in properties) + { + adapter.AppendColumnName(sqlStringBuilder, property.ColumnName()); + sqlStringBuilder.Append(" as "); + adapter.AppendColumnName(sqlStringBuilder, property.Name); + sqlStringBuilder.AppendLine(","); + } + + sqlStringBuilder.Remove(sqlStringBuilder.Length - 3, 2); + + sqlStringBuilder.Append($" from {name} where "); + adapter.AppendColumnNameEqualsValue(sqlStringBuilder, key.ColumnName(), "id"); - sql = $"SELECT * FROM {name} WHERE {key.Name} = @id"; - GetQueries[type.TypeHandle] = sql; + sql = sqlStringBuilder.ToString(); + + GetQueries[getQueriesKey] = sql; } var dynParams = new DynamicParameters(); @@ -47,7 +68,7 @@ public static async Task GetAsync(this IDbConnection connection, dynamic i var obj = ProxyGenerator.GetInterfaceProxy(); - foreach (var property in TypePropertiesCache(type)) + foreach (var property in properties) { var val = res[property.Name]; if (val == null) continue; @@ -82,14 +103,32 @@ public static Task> GetAllAsync(this IDbConnection connection, { var type = typeof(T); var cacheType = typeof(List); + var properties = TypePropertiesCache(type); + var adapter = GetFormatter(connection); - if (!GetQueries.TryGetValue(cacheType.TypeHandle, out string sql)) + var getQueriesKey = new GetQueriesCacheKey(cacheType.TypeHandle, adapter.GetType().TypeHandle); + + if (!GetQueries.TryGetValue(getQueriesKey, out string sql)) { GetSingleKey(nameof(GetAll)); - var name = GetTableName(type); + var name = type.TableName(); + + StringBuilder sqlStringBuilder = new StringBuilder("select "); + + foreach (PropertyInfo property in properties) + { + adapter.AppendColumnName(sqlStringBuilder, property.ColumnName()); + sqlStringBuilder.Append(" as "); + adapter.AppendColumnName(sqlStringBuilder, property.Name); + sqlStringBuilder.AppendLine(","); + } + + sqlStringBuilder.Remove(sqlStringBuilder.Length - 3, 2); + + sqlStringBuilder.Append($" from {name} "); - sql = "SELECT * FROM " + name; - GetQueries[cacheType.TypeHandle] = sql; + sql = sqlStringBuilder.ToString(); + GetQueries[getQueriesKey] = sql; } if (!type.IsInterface) @@ -108,7 +147,7 @@ private static async Task> GetAllAsyncImpl(IDbConnection conne var obj = ProxyGenerator.GetInterfaceProxy(); foreach (var property in TypePropertiesCache(type)) { - var val = res[property.Name]; + var val = res[property.ColumnName()]; if (val == null) continue; if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) { @@ -162,7 +201,7 @@ public static Task InsertAsync(this IDbConnection connection, T entityTo } } - var name = GetTableName(type); + var name = type.TableName(); var sbColumnList = new StringBuilder(null); var allProperties = TypePropertiesCache(type); var keyProperties = KeyPropertiesCache(type).ToList(); @@ -172,7 +211,7 @@ public static Task InsertAsync(this IDbConnection connection, T entityTo for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++) { var property = allPropertiesExceptKeyAndComputed[i]; - sqlAdapter.AppendColumnName(sbColumnList, property.Name); + sqlAdapter.AppendColumnName(sbColumnList, property.ColumnName()); if (i < allPropertiesExceptKeyAndComputed.Count - 1) sbColumnList.Append(", "); } @@ -237,7 +276,7 @@ public static async Task UpdateAsync(this IDbConnection connection, T e if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0) throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property"); - var name = GetTableName(type); + var name = type.TableName(); var sb = new StringBuilder(); sb.AppendFormat("update {0} set ", name); @@ -252,7 +291,7 @@ public static async Task UpdateAsync(this IDbConnection connection, T e for (var i = 0; i < nonIdProps.Count; i++) { var property = nonIdProps[i]; - adapter.AppendColumnNameEqualsValue(sb, property.Name); + adapter.AppendColumnNameEqualsValue(sb, property.ColumnName(), property.Name); if (i < nonIdProps.Count - 1) sb.Append(", "); } @@ -260,7 +299,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.ColumnName(), property.Name); if (i < keyProperties.Count - 1) sb.Append(" and "); } @@ -306,7 +345,7 @@ public static async Task DeleteAsync(this IDbConnection connection, T e if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0) throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property"); - var name = GetTableName(type); + var name = type.TableName(); var allKeyProperties = keyProperties.Concat(explicitKeyProperties).ToList(); var sb = new StringBuilder(); @@ -317,7 +356,7 @@ public static async Task DeleteAsync(this IDbConnection connection, T e for (var i = 0; i < allKeyProperties.Count; i++) { var property = allKeyProperties[i]; - adapter.AppendColumnNameEqualsValue(sb, property.Name); + adapter.AppendColumnNameEqualsValue(sb, property.ColumnName(), property.Name); if (i < allKeyProperties.Count - 1) sb.Append(" AND "); } @@ -336,7 +375,7 @@ public static async Task DeleteAsync(this IDbConnection connection, T e public static async Task DeleteAllAsync(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null) where T : class { var type = typeof(T); - var statement = "DELETE FROM " + GetTableName(type); + var statement = "DELETE FROM " + type.TableName(); var deleted = await connection.ExecuteAsync(statement, null, transaction, commandTimeout).ConfigureAwait(false); return deleted > 0; } diff --git a/src/Dapper.Contrib/SqlMapperExtensions.cs b/src/Dapper.Contrib/SqlMapperExtensions.cs index 9a30e805..d2b91530 100644 --- a/src/Dapper.Contrib/SqlMapperExtensions.cs +++ b/src/Dapper.Contrib/SqlMapperExtensions.cs @@ -46,18 +46,12 @@ public interface ITableNameMapper /// /// The connection to get a database type name from. public delegate string GetDatabaseTypeDelegate(IDbConnection connection); - /// - /// The function to get a table name from a given - /// - /// The to get a table name for. - public delegate string TableNameMapperDelegate(Type type); - private static readonly ConcurrentDictionary> KeyProperties = new ConcurrentDictionary>(); - private static readonly ConcurrentDictionary> ExplicitKeyProperties = new ConcurrentDictionary>(); - private static readonly ConcurrentDictionary> TypeProperties = new ConcurrentDictionary>(); - private static readonly ConcurrentDictionary> ComputedProperties = new ConcurrentDictionary>(); - private static readonly ConcurrentDictionary GetQueries = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary TypeTableName = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary> KeyProperties = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> ExplicitKeyProperties = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> TypeProperties = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> ComputedProperties = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary GetQueries = new ConcurrentDictionary(); private static readonly ISqlAdapter DefaultAdapter = new SqlServerAdapter(); private static readonly Dictionary AdapterDictionary @@ -73,9 +67,9 @@ private static readonly Dictionary AdapterDictionary private static List ComputedPropertiesCache(Type type) { - if (ComputedProperties.TryGetValue(type.TypeHandle, out IEnumerable pi)) + if (ComputedProperties.TryGetValue(type.TypeHandle, out List pi)) { - return pi.ToList(); + return pi; } var computedProperties = TypePropertiesCache(type).Where(p => p.GetCustomAttributes(true).Any(a => a is ComputedAttribute)).ToList(); @@ -86,9 +80,9 @@ private static List ComputedPropertiesCache(Type type) private static List ExplicitKeyPropertiesCache(Type type) { - if (ExplicitKeyProperties.TryGetValue(type.TypeHandle, out IEnumerable pi)) + if (ExplicitKeyProperties.TryGetValue(type.TypeHandle, out List pi)) { - return pi.ToList(); + return pi; } var explicitKeyProperties = TypePropertiesCache(type).Where(p => p.GetCustomAttributes(true).Any(a => a is ExplicitKeyAttribute)).ToList(); @@ -99,9 +93,9 @@ private static List ExplicitKeyPropertiesCache(Type type) private static List KeyPropertiesCache(Type type) { - if (KeyProperties.TryGetValue(type.TypeHandle, out IEnumerable pi)) + if (KeyProperties.TryGetValue(type.TypeHandle, out List pi)) { - return pi.ToList(); + return pi; } var allProperties = TypePropertiesCache(type); @@ -109,7 +103,7 @@ private static List KeyPropertiesCache(Type type) if (keyProperties.Count == 0) { - var idProp = allProperties.Find(p => string.Equals(p.Name, "id", StringComparison.CurrentCultureIgnoreCase)); + var idProp = allProperties.Find(p => string.Equals(p.ColumnName(), "id", StringComparison.CurrentCultureIgnoreCase)); if (idProp != null && !idProp.GetCustomAttributes(true).Any(a => a is ExplicitKeyAttribute)) { keyProperties.Add(idProp); @@ -122,12 +116,12 @@ private static List KeyPropertiesCache(Type type) private static List TypePropertiesCache(Type type) { - if (TypeProperties.TryGetValue(type.TypeHandle, out IEnumerable pis)) + if (TypeProperties.TryGetValue(type.TypeHandle, out List pis)) { - return pis.ToList(); + return pis; } - var properties = type.GetProperties().Where(IsWriteable).ToArray(); + var properties = type.GetProperties().Where(IsWriteable).ToList(); TypeProperties[type.TypeHandle] = properties; return properties.ToList(); } @@ -170,14 +164,34 @@ private static PropertyInfo GetSingleKey(string method) public static T Get(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int? commandTimeout = null) where T : class { var type = typeof(T); + var properties = TypePropertiesCache(type); + var adapter = GetFormatter(connection); + + var getQueriesKey = new GetQueriesCacheKey(type.TypeHandle, adapter.GetType().TypeHandle); - if (!GetQueries.TryGetValue(type.TypeHandle, out string sql)) + if (!GetQueries.TryGetValue(getQueriesKey, out string sql)) { var key = GetSingleKey(nameof(Get)); - var name = GetTableName(type); + var name = type.TableName(); + + StringBuilder sqlStringBuilder = new StringBuilder("select "); + + foreach (PropertyInfo property in properties) + { + adapter.AppendColumnName(sqlStringBuilder, property.ColumnName()); + sqlStringBuilder.Append(" as "); + adapter.AppendColumnName(sqlStringBuilder, property.Name); + sqlStringBuilder.AppendLine(","); + } - sql = $"select * from {name} where {key.Name} = @id"; - GetQueries[type.TypeHandle] = sql; + sqlStringBuilder.Remove(sqlStringBuilder.Length - 3, 2); + + sqlStringBuilder.Append($" from {name} where "); + adapter.AppendColumnNameEqualsValue(sqlStringBuilder, key.ColumnName(), "id"); + + sql = sqlStringBuilder.ToString(); + + GetQueries[getQueriesKey] = sql; } var dynParams = new DynamicParameters(); @@ -194,7 +208,7 @@ public static T Get(this IDbConnection connection, dynamic id, IDbTransaction obj = ProxyGenerator.GetInterfaceProxy(); - foreach (var property in TypePropertiesCache(type)) + foreach (var property in properties) { var val = res[property.Name]; if (val == null) continue; @@ -233,14 +247,31 @@ public static IEnumerable GetAll(this IDbConnection connection, IDbTransac { var type = typeof(T); var cacheType = typeof(List); + var adapter = GetFormatter(connection); + var properties = TypePropertiesCache(type); + var getQueriesKey = new GetQueriesCacheKey(cacheType.TypeHandle, adapter.GetType().TypeHandle); - if (!GetQueries.TryGetValue(cacheType.TypeHandle, out string sql)) + if (!GetQueries.TryGetValue(getQueriesKey, out string sql)) { GetSingleKey(nameof(GetAll)); - var name = GetTableName(type); + var name = type.TableName(); - sql = "select * from " + name; - GetQueries[cacheType.TypeHandle] = sql; + StringBuilder sqlStringBuilder = new StringBuilder("select "); + + foreach (PropertyInfo property in properties) + { + adapter.AppendColumnName(sqlStringBuilder, property.ColumnName()); + sqlStringBuilder.Append(" as "); + adapter.AppendColumnName(sqlStringBuilder, property.Name); + sqlStringBuilder.AppendLine(","); + } + + sqlStringBuilder.Remove(sqlStringBuilder.Length - 3, 2); + + sqlStringBuilder.Append($" from {name} "); + + sql = sqlStringBuilder.ToString(); + GetQueries[getQueriesKey] = sql; } if (!type.IsInterface) return connection.Query(sql, null, transaction, commandTimeout: commandTimeout); @@ -250,9 +281,9 @@ public static IEnumerable GetAll(this IDbConnection connection, IDbTransac foreach (IDictionary res in result) { var obj = ProxyGenerator.GetInterfaceProxy(); - foreach (var property in TypePropertiesCache(type)) + foreach (var property in properties) { - var val = res[property.Name]; + var val = res[property.ColumnName()]; if (val == null) continue; if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) { @@ -274,39 +305,13 @@ public static IEnumerable GetAll(this IDbConnection connection, IDbTransac /// Specify a custom table name mapper based on the POCO type name /// #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 TableNameMapperDelegate TableNameMapper; -#pragma warning restore CA2211 // Non-constant fields should not be visible - - private static string GetTableName(Type type) + [Obsolete("Use TypeExtensions.TableNameMapper instead")] + public static TypeExtensions.TableNameMapperDelegate TableNameMapper { - if (TypeTableName.TryGetValue(type.TypeHandle, out string name)) return name; - - if (TableNameMapper != null) - { - name = TableNameMapper(type); - } - else - { - //NOTE: This as dynamic trick falls back to handle both our own Table-attribute as well as the one in EntityFramework - var tableAttrName = - type.GetCustomAttribute(false)?.Name - ?? (type.GetCustomAttributes(false).FirstOrDefault(attr => attr.GetType().Name == "TableAttribute") as dynamic)?.Name; - - if (tableAttrName != null) - { - name = tableAttrName; - } - else - { - name = type.Name + "s"; - if (type.IsInterface && name.StartsWith("I")) - name = name.Substring(1); - } - } - - TypeTableName[type.TypeHandle] = name; - return name; + get => TypeExtensions.TableNameMapper; + set => TypeExtensions.TableNameMapper = value; } +#pragma warning restore CA2211 // Non-constant fields should not be visible /// /// Inserts an entity into table "Ts" and returns identity id or number of inserted rows if inserting a list. @@ -342,7 +347,7 @@ public static long Insert(this IDbConnection connection, T entityToInsert, ID } } - var name = GetTableName(type); + var name = type.TableName(); var sbColumnList = new StringBuilder(null); var allProperties = TypePropertiesCache(type); var keyProperties = KeyPropertiesCache(type); @@ -354,7 +359,7 @@ public static long Insert(this IDbConnection connection, T entityToInsert, ID for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++) { var property = allPropertiesExceptKeyAndComputed[i]; - adapter.AppendColumnName(sbColumnList, property.Name); //fix for issue #336 + adapter.AppendColumnName(sbColumnList, property.ColumnName()); //fix for issue #336 if (i < allPropertiesExceptKeyAndComputed.Count - 1) sbColumnList.Append(", "); } @@ -427,7 +432,7 @@ public static bool Update(this IDbConnection connection, T entityToUpdate, ID if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0) throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property"); - var name = GetTableName(type); + var name = type.TableName(); var sb = new StringBuilder(); sb.AppendFormat("update {0} set ", name); @@ -442,7 +447,7 @@ public static bool Update(this IDbConnection connection, T entityToUpdate, ID 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.ColumnName(), property.Name); //fix for issue #336 if (i < nonIdProps.Count - 1) sb.Append(", "); } @@ -450,7 +455,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.ColumnName(), property.Name); //fix for issue #336 if (i < keyProperties.Count - 1) sb.Append(" and "); } @@ -496,7 +501,7 @@ public static bool Delete(this IDbConnection connection, T entityToDelete, ID if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0) throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property"); - var name = GetTableName(type); + var name = type.TableName(); keyProperties.AddRange(explicitKeyProperties); var sb = new StringBuilder(); @@ -507,7 +512,7 @@ public static bool Delete(this IDbConnection connection, T entityToDelete, 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.ColumnName(), property.Name); //fix for issue #336 if (i < keyProperties.Count - 1) sb.Append(" and "); } @@ -526,7 +531,7 @@ public static bool Delete(this IDbConnection connection, T entityToDelete, ID public static bool DeleteAll(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null) where T : class { var type = typeof(T); - var name = GetTableName(type); + var name = type.TableName(); var statement = $"delete from {name}"; var deleted = connection.Execute(statement, null, transaction, commandTimeout); return deleted > 0; @@ -721,6 +726,27 @@ public TableAttribute(string tableName) public string Name { get; set; } } + /// + /// Defines the name of a column to use in Dapper.Contrib commands. + /// + [AttributeUsage(AttributeTargets.Property, Inherited = true)] + public class ColumnAttribute : Attribute + { + /// + /// Creates a column mapping to a specific name for Dapper.Contrib commands + /// + /// The name of this column in the database. + public ColumnAttribute(string columnName) + { + Name = columnName; + } + + /// + /// The name of the column in the database + /// + public string Name { get; set; } + } + /// /// Specifies that this field is a primary key in the database /// @@ -765,6 +791,31 @@ public WriteAttribute(bool write) public class ComputedAttribute : Attribute { } + + internal readonly struct GetQueriesCacheKey : IEquatable + { + public bool Equals(GetQueriesCacheKey other) => DataType.Equals(other.DataType) && AdapterType.Equals(other.AdapterType); + + public override bool Equals(object obj) => obj is GetQueriesCacheKey other && Equals(other); + + public override int GetHashCode() + { + unchecked + { + return (DataType.GetHashCode() * 397) ^ AdapterType.GetHashCode(); + } + } + + public GetQueriesCacheKey(RuntimeTypeHandle dataType, RuntimeTypeHandle adapterType) + { + DataType = dataType; + AdapterType = adapterType; + } + + public RuntimeTypeHandle DataType { get; } + + public RuntimeTypeHandle AdapterType { get; } + } } /// @@ -796,9 +847,10 @@ public partial interface ISqlAdapter /// /// Adds a column equality to a parameter. /// - /// The string builder to append to. + /// The string builder to append to. /// The column name. - void AppendColumnNameEqualsValue(StringBuilder sb, string columnName); + /// The value parameter name. + void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string propertyName); } /// @@ -851,9 +903,10 @@ public void AppendColumnName(StringBuilder sb, string columnName) /// /// The string builder to append to. /// The column name. - public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + /// The value parameter name. + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string propertyName) { - sb.AppendFormat("[{0}] = @{1}", columnName, columnName); + sb.AppendFormat("[{0}] = @{1}", columnName, propertyName); } } @@ -907,9 +960,10 @@ public void AppendColumnName(StringBuilder sb, string columnName) /// /// The string builder to append to. /// The column name. - public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + /// The value parameter name. + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string propertyName) { - sb.AppendFormat("[{0}] = @{1}", columnName, columnName); + sb.AppendFormat("[{0}] = @{1}", columnName, propertyName); } } @@ -962,9 +1016,10 @@ public void AppendColumnName(StringBuilder sb, string columnName) /// /// The string builder to append to. /// The column name. - public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + /// The value parameter name. + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string propertyName) { - sb.AppendFormat("`{0}` = @{1}", columnName, columnName); + sb.AppendFormat("`{0}` = @{1}", columnName, propertyName); } } @@ -1038,9 +1093,10 @@ public void AppendColumnName(StringBuilder sb, string columnName) /// /// The string builder to append to. /// The column name. - public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + /// The value parameter name. + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string propertyName) { - sb.AppendFormat("\"{0}\" = @{1}", columnName, columnName); + sb.AppendFormat("\"{0}\" = @{1}", columnName, propertyName); } } @@ -1091,9 +1147,10 @@ public void AppendColumnName(StringBuilder sb, string columnName) /// /// The string builder to append to. /// The column name. - public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + /// The value parameter name. + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string propertyName) { - sb.AppendFormat("\"{0}\" = @{1}", columnName, columnName); + sb.AppendFormat("\"{0}\" = @{1}", columnName, propertyName); } } @@ -1148,8 +1205,9 @@ public void AppendColumnName(StringBuilder sb, string columnName) /// /// The string builder to append to. /// The column name. - public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName) + /// The value parameter name. + public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName, string propertyName) { - sb.AppendFormat("{0} = @{1}", columnName, columnName); + sb.AppendFormat("{0} = @{1}", columnName, propertyName); } } diff --git a/src/Dapper.Contrib/TypeExtensions.cs b/src/Dapper.Contrib/TypeExtensions.cs new file mode 100644 index 00000000..5d7afa90 --- /dev/null +++ b/src/Dapper.Contrib/TypeExtensions.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Reflection; + +namespace Dapper.Contrib.Extensions +{ + /// + /// Map to database table extensions + /// + public static class TypeExtensions + { + private static readonly ConcurrentDictionary TypeTableName = new ConcurrentDictionary(); + + /// + /// The function to get a table name from a given + /// + /// The to get a table name for. + public delegate string TableNameMapperDelegate(Type type); + + /// + /// Specify a custom table name mapper based on the POCO type name + /// +#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 TableNameMapperDelegate TableNameMapper; +#pragma warning restore CA2211 // Non-constant fields should not be visible + + /// + /// Get database table name from + /// + /// The to get a table name for. + public static string TableName(this Type type) + { + if (TypeTableName.TryGetValue(type.TypeHandle, out string name)) return name; + + if (TableNameMapper != null) + { + name = TableNameMapper(type); + } + else + { + //NOTE: This as dynamic trick falls back to handle both our own Table-attribute as well as the one in EntityFramework + var tableAttrName = + type.GetCustomAttribute(false)?.Name + ?? (type.GetCustomAttributes(false).FirstOrDefault(attr => attr.GetType().Name == "TableAttribute") as dynamic)?.Name; + + if (tableAttrName != null) + { + name = tableAttrName; + } + else + { + name = type.Name + "s"; + if (type.IsInterface && name.StartsWith("I")) + name = name.Substring(1); + } + } + + TypeTableName[type.TypeHandle] = name; + return name; + } + } +} diff --git a/tests/Dapper.Tests.Contrib/TestSuite.Async.cs b/tests/Dapper.Tests.Contrib/TestSuite.Async.cs index fcd11802..fc04c76c 100644 --- a/tests/Dapper.Tests.Contrib/TestSuite.Async.cs +++ b/tests/Dapper.Tests.Contrib/TestSuite.Async.cs @@ -106,7 +106,7 @@ public async Task InsertGetUpdateDeleteWithExplicitKeyAsync() var o1 = new ObjectX { ObjectXId = guid, Name = "Foo" }; var originalxCount = (await connection.QueryAsync("Select Count(*) From ObjectX").ConfigureAwait(false)).First(); await connection.InsertAsync(o1).ConfigureAwait(false); - var list1 = (await connection.QueryAsync("select * from ObjectX").ConfigureAwait(false)).ToList(); + var list1 = (await connection.GetAllAsync().ConfigureAwait(false)).ToList(); Assert.Equal(list1.Count, originalxCount + 1); o1 = await connection.GetAsync(guid).ConfigureAwait(false); Assert.Equal(o1.ObjectXId, guid); @@ -122,7 +122,7 @@ public async Task InsertGetUpdateDeleteWithExplicitKeyAsync() var o2 = new ObjectY { ObjectYId = id, Name = "Foo" }; var originalyCount = connection.Query("Select Count(*) From ObjectY").First(); await connection.InsertAsync(o2).ConfigureAwait(false); - var list2 = (await connection.QueryAsync("select * from ObjectY").ConfigureAwait(false)).ToList(); + var list2 = (await connection.GetAllAsync().ConfigureAwait(false)).ToList(); Assert.Equal(list2.Count, originalyCount + 1); o2 = await connection.GetAsync(id).ConfigureAwait(false); Assert.Equal(o2.ObjectYId, id); @@ -142,12 +142,14 @@ public async Task TableNameAsync() using (var connection = GetOpenConnection()) { // tests against "Automobiles" table (Table attribute) - var id = await connection.InsertAsync(new Car { Name = "VolvoAsync" }).ConfigureAwait(false); + var id = await connection.InsertAsync(new Car { Model = "VolvoAsync" }).ConfigureAwait(false); var car = await connection.GetAsync(id).ConfigureAwait(false); Assert.NotNull(car); - Assert.Equal("VolvoAsync", car.Name); - Assert.True(await connection.UpdateAsync(new Car { Id = id, Name = "SaabAsync" }).ConfigureAwait(false)); - Assert.Equal("SaabAsync", (await connection.GetAsync(id).ConfigureAwait(false)).Name); + Assert.Equal("VolvoAsync", car.Model); + Assert.Equal("[VolvoAsync]", car.Computed); + Assert.True(await connection.UpdateAsync(new Car { Id = id, Model = "SaabAsync" }).ConfigureAwait(false)); + Assert.Equal("SaabAsync", (await connection.GetAsync(id).ConfigureAwait(false)).Model); + Assert.Equal("[SaabAsync]", (await connection.GetAsync(id).ConfigureAwait(false)).Computed); Assert.True(await connection.DeleteAsync(new Car { Id = id }).ConfigureAwait(false)); Assert.Null(await connection.GetAsync(id).ConfigureAwait(false)); } @@ -158,10 +160,10 @@ public async Task TestSimpleGetAsync() { using (var connection = GetOpenConnection()) { - var id = await connection.InsertAsync(new User { Name = "Adama", Age = 10 }).ConfigureAwait(false); + var id = await connection.InsertAsync(new User { FullName = "Adama", Age = 10 }).ConfigureAwait(false); var user = await connection.GetAsync(id).ConfigureAwait(false); Assert.Equal(id, user.Id); - Assert.Equal("Adama", user.Name); + Assert.Equal("Adama", user.FullName); await connection.DeleteAsync(user).ConfigureAwait(false); } } @@ -175,33 +177,33 @@ public async Task InsertGetUpdateAsync() var originalCount = (await connection.QueryAsync("select Count(*) from Users").ConfigureAwait(false)).First(); - var id = await connection.InsertAsync(new User { Name = "Adam", Age = 10 }).ConfigureAwait(false); + var id = await connection.InsertAsync(new User { FullName = "Adam", Age = 10 }).ConfigureAwait(false); //get a user with "isdirty" tracking var user = await connection.GetAsync(id).ConfigureAwait(false); - Assert.Equal("Adam", user.Name); + Assert.Equal("Adam", user.FullName); Assert.False(await connection.UpdateAsync(user).ConfigureAwait(false)); //returns false if not updated, based on tracking - user.Name = "Bob"; + user.FullName = "Bob"; Assert.True(await connection.UpdateAsync(user).ConfigureAwait(false)); //returns true if updated, based on tracking user = await connection.GetAsync(id).ConfigureAwait(false); - Assert.Equal("Bob", user.Name); + Assert.Equal("Bob", user.FullName); //get a user with no tracking var notrackedUser = await connection.GetAsync(id).ConfigureAwait(false); - Assert.Equal("Bob", notrackedUser.Name); + Assert.Equal("Bob", notrackedUser.FullName); Assert.True(await connection.UpdateAsync(notrackedUser).ConfigureAwait(false)); //returns true, even though user was not changed - notrackedUser.Name = "Cecil"; + notrackedUser.FullName = "Cecil"; Assert.True(await connection.UpdateAsync(notrackedUser).ConfigureAwait(false)); - Assert.Equal("Cecil", (await connection.GetAsync(id).ConfigureAwait(false)).Name); + Assert.Equal("Cecil", (await connection.GetAsync(id).ConfigureAwait(false)).FullName); - Assert.Equal((await connection.QueryAsync("select * from Users").ConfigureAwait(false)).Count(), originalCount + 1); + Assert.Equal((await connection.GetAllAsync().ConfigureAwait(false)).Count(), originalCount + 1); Assert.True(await connection.DeleteAsync(user).ConfigureAwait(false)); - Assert.Equal((await connection.QueryAsync("select * from Users").ConfigureAwait(false)).Count(), originalCount); + Assert.Equal((await connection.GetAllAsync().ConfigureAwait(false)).Count(), originalCount); Assert.False(await connection.UpdateAsync(notrackedUser).ConfigureAwait(false)); //returns false, user not found - Assert.True(await connection.InsertAsync(new User { Name = "Adam", Age = 10 }).ConfigureAwait(false) > originalCount + 1); + Assert.True(await connection.InsertAsync(new User { FullName = "Adam", Age = 10 }).ConfigureAwait(false) > originalCount + 1); } } @@ -213,7 +215,7 @@ public async Task InsertCheckKeyAsync() await connection.DeleteAllAsync().ConfigureAwait(false); Assert.Null(await connection.GetAsync(3).ConfigureAwait(false)); - var user = new User { Name = "Adamb", Age = 10 }; + var user = new User { FullName = "Adamb", Age = 10 }; var id = await connection.InsertAsync(user).ConfigureAwait(false); Assert.Equal(user.Id, id); } @@ -230,24 +232,23 @@ public async Task BuilderSelectClauseAsync() var data = new List(100); for (var i = 0; i < 100; i++) { - var nU = new User { Age = rand.Next(70), Id = i, Name = Guid.NewGuid().ToString() }; + var nU = new User { Age = rand.Next(70), Id = i, FullName = Guid.NewGuid().ToString() }; data.Add(nU); nU.Id = await connection.InsertAsync(nU).ConfigureAwait(false); } var builder = new SqlBuilder(); var justId = builder.AddTemplate("SELECT /**select**/ FROM Users"); - var all = builder.AddTemplate("SELECT Name, /**select**/, Age FROM Users"); builder.Select("Id"); var ids = await connection.QueryAsync(justId.RawSql, justId.Parameters).ConfigureAwait(false); - var users = await connection.QueryAsync(all.RawSql, all.Parameters).ConfigureAwait(false); + var users = await connection.GetAllAsync().ConfigureAwait(false); foreach (var u in data) { if (!ids.Any(i => u.Id == i)) throw new Exception("Missing ids in select"); - if (!users.Any(a => a.Id == u.Id && a.Name == u.Name && a.Age == u.Age)) + if (!users.Any(a => a.Id == u.Id && a.FullName == u.FullName && a.Age == u.Age)) throw new Exception("Missing users in select"); } } @@ -266,7 +267,7 @@ public async Task BuilderTemplateWithoutCompositionAsync() { await connection.DeleteAllAsync().ConfigureAwait(false); - await connection.InsertAsync(new User { Age = 5, Name = "Testy McTestington" }).ConfigureAwait(false); + await connection.InsertAsync(new User { Age = 5, FullName = "Testy McTestington" }).ConfigureAwait(false); if ((await connection.QueryAsync(template.RawSql, template.Parameters).ConfigureAwait(false)).Single() != 1) throw new Exception("Query failed"); @@ -298,7 +299,7 @@ private async Task InsertHelperAsync(Func, T> helper) var users = new List(numberOfEntities); for (var i = 0; i < numberOfEntities; i++) - users.Add(new User { Name = "User " + i, Age = i }); + users.Add(new User { FullName = "User " + i, Age = i }); using (var connection = GetOpenConnection()) { @@ -306,7 +307,7 @@ private async Task InsertHelperAsync(Func, T> helper) var total = await connection.InsertAsync(helper(users)).ConfigureAwait(false); Assert.Equal(total, numberOfEntities); - users = connection.Query("select * from Users").ToList(); + users = connection.GetAll().ToList(); Assert.Equal(users.Count, numberOfEntities); } } @@ -336,7 +337,7 @@ private async Task UpdateHelperAsync(Func, T> helper) var users = new List(numberOfEntities); for (var i = 0; i < numberOfEntities; i++) - users.Add(new User { Name = "User " + i, Age = i }); + users.Add(new User { FullName = "User " + i, Age = i }); using (var connection = GetOpenConnection()) { @@ -344,14 +345,14 @@ private async Task UpdateHelperAsync(Func, T> helper) var total = await connection.InsertAsync(helper(users)).ConfigureAwait(false); Assert.Equal(total, numberOfEntities); - users = connection.Query("select * from Users").ToList(); + users = connection.GetAll().ToList(); Assert.Equal(users.Count, numberOfEntities); foreach (var user in users) { - user.Name += " updated"; + user.FullName += " updated"; } await connection.UpdateAsync(helper(users)).ConfigureAwait(false); - var name = connection.Query("select * from Users").First().Name; + var name = connection.GetAll().First().FullName; Assert.Contains("updated", name); } } @@ -381,7 +382,7 @@ private async Task DeleteHelperAsync(Func, T> helper) var users = new List(numberOfEntities); for (var i = 0; i < numberOfEntities; i++) - users.Add(new User { Name = "User " + i, Age = i }); + users.Add(new User { FullName = "User " + i, Age = i }); using (var connection = GetOpenConnection()) { @@ -389,12 +390,12 @@ private async Task DeleteHelperAsync(Func, T> helper) var total = await connection.InsertAsync(helper(users)).ConfigureAwait(false); Assert.Equal(total, numberOfEntities); - users = connection.Query("select * from Users").ToList(); + users = connection.GetAll().ToList(); Assert.Equal(users.Count, numberOfEntities); var usersToDelete = users.Take(10).ToList(); await connection.DeleteAsync(helper(usersToDelete)).ConfigureAwait(false); - users = connection.Query("select * from Users").ToList(); + users = connection.GetAll().ToList(); Assert.Equal(users.Count, numberOfEntities - 10); } } @@ -406,7 +407,7 @@ public async Task GetAllAsync() var users = new List(numberOfEntities); for (var i = 0; i < numberOfEntities; i++) - users.Add(new User { Name = "User " + i, Age = i }); + users.Add(new User { FullName = "User " + i, Age = i }); using (var connection = GetOpenConnection()) { @@ -465,8 +466,8 @@ public async Task DeleteAllAsync() { await connection.DeleteAllAsync().ConfigureAwait(false); - var id1 = await connection.InsertAsync(new User { Name = "Alice", Age = 32 }).ConfigureAwait(false); - var id2 = await connection.InsertAsync(new User { Name = "Bob", Age = 33 }).ConfigureAwait(false); + var id1 = await connection.InsertAsync(new User { FullName = "Alice", Age = 32 }).ConfigureAwait(false); + var id2 = await connection.InsertAsync(new User { FullName = "Bob", Age = 33 }).ConfigureAwait(false); await connection.DeleteAllAsync().ConfigureAwait(false); Assert.Null(await connection.GetAsync(id1).ConfigureAwait(false)); Assert.Null(await connection.GetAsync(id2).ConfigureAwait(false)); diff --git a/tests/Dapper.Tests.Contrib/TestSuite.cs b/tests/Dapper.Tests.Contrib/TestSuite.cs index 71dbd8ad..826d6c55 100644 --- a/tests/Dapper.Tests.Contrib/TestSuite.cs +++ b/tests/Dapper.Tests.Contrib/TestSuite.cs @@ -37,14 +37,16 @@ public interface IUser { [Key] int Id { get; set; } - string Name { get; set; } + [Column("Name")] + string FullName { get; set; } int Age { get; set; } } public class User : IUser { public int Id { get; set; } - public string Name { get; set; } + [Column("Name")] + public string FullName { get; set; } public int Age { get; set; } } @@ -63,7 +65,8 @@ public class NullableDate : INullableDate public class Person { - public int Id { get; set; } + [Column("Id")] + public int IdProp { get; set; } public string Name { get; set; } } @@ -71,7 +74,8 @@ public class Person public class Stuff { [Key] - public short TheId { get; set; } + [Column("TheId")] + public short ShortId { get; set; } public string Name { get; set; } public DateTime? Created { get; set; } } @@ -80,7 +84,8 @@ public class Stuff public class Car { public int Id { get; set; } - public string Name { get; set; } + [Column("Name")] + public string Model { get; set; } [Computed] public string Computed { get; set; } } @@ -228,7 +233,7 @@ public void InsertGetUpdateDeleteWithExplicitKey() var o1 = new ObjectX { ObjectXId = guid, Name = "Foo" }; var originalxCount = connection.Query("Select Count(*) From ObjectX").First(); connection.Insert(o1); - var list1 = connection.Query("select * from ObjectX").ToList(); + var list1 = connection.GetAll().ToList(); Assert.Equal(list1.Count, originalxCount + 1); o1 = connection.Get(guid); Assert.Equal(o1.ObjectXId, guid); @@ -244,7 +249,7 @@ public void InsertGetUpdateDeleteWithExplicitKey() var o2 = new ObjectY { ObjectYId = id, Name = "Foo" }; var originalyCount = connection.Query("Select Count(*) From ObjectY").First(); connection.Insert(o2); - var list2 = connection.Query("select * from ObjectY").ToList(); + var list2 = connection.GetAll().ToList(); Assert.Equal(list2.Count, originalyCount + 1); o2 = connection.Get(id); Assert.Equal(o2.ObjectYId, id); @@ -281,7 +286,7 @@ public void InsertGetUpdateDeleteWithExplicitKeyNamedId() const int id = 42; var o2 = new ObjectZ { Id = id, Name = "Foo" }; connection.Insert(o2); - var list2 = connection.Query("select * from ObjectZ").ToList(); + var list2 = connection.GetAll().ToList(); Assert.Single(list2); o2 = connection.Get(id); Assert.Equal(o2.Id, id); @@ -297,7 +302,7 @@ public void ShortIdentity() var id = connection.Insert(new Stuff { Name = name }); Assert.True(id > 0); // 1-n are valid here, due to parallel tests var item = connection.Get(id); - Assert.Equal(item.TheId, (short)id); + Assert.Equal(item.ShortId, (short)id); Assert.Equal(item.Name, name); } } @@ -309,7 +314,7 @@ public void NullDateTime() { connection.Insert(new Stuff { Name = "First item" }); connection.Insert(new Stuff { Name = "Second item", Created = DateTime.Now }); - var stuff = connection.Query("select * from Stuff").ToList(); + var stuff = connection.GetAll().ToList(); Assert.Null(stuff[0].Created); Assert.NotNull(stuff.Last().Created); } @@ -321,13 +326,16 @@ public void TableName() using (var connection = GetOpenConnection()) { // tests against "Automobiles" table (Table attribute) - var id = connection.Insert(new Car { Name = "Volvo" }); + var id = connection.Insert(new Car { Model = "Volvo" }); var car = connection.Get(id); Assert.NotNull(car); - Assert.Equal("Volvo", car.Name); - Assert.Equal("Volvo", connection.Get(id).Name); - Assert.True(connection.Update(new Car { Id = (int)id, Name = "Saab" })); - Assert.Equal("Saab", connection.Get(id).Name); + Assert.Equal("Volvo", car.Model); + Assert.Equal("[Volvo]", car.Computed); + Assert.Equal("Volvo", connection.Get(id).Model); + Assert.Equal("[Volvo]", connection.Get(id).Computed); + Assert.True(connection.Update(new Car { Id = (int)id, Model = "Saab" })); + Assert.Equal("Saab", connection.Get(id).Model); + Assert.Equal("[Saab]", connection.Get(id).Computed); Assert.True(connection.Delete(new Car { Id = (int)id })); Assert.Null(connection.Get(id)); } @@ -338,10 +346,10 @@ public void TestSimpleGet() { using (var connection = GetOpenConnection()) { - var id = connection.Insert(new User { Name = "Adama", Age = 10 }); + var id = connection.Insert(new User { FullName = "Adama", Age = 10 }); var user = connection.Get(id); Assert.Equal(user.Id, (int)id); - Assert.Equal("Adama", user.Name); + Assert.Equal("Adama", user.FullName); connection.Delete(user); } } @@ -351,7 +359,7 @@ public void TestClosedConnection() { using (var connection = GetConnection()) { - Assert.True(connection.Insert(new User { Name = "Adama", Age = 10 }) > 0); + Assert.True(connection.Insert(new User { FullName = "Adama", Age = 10 }) > 0); var users = connection.GetAll(); Assert.NotEmpty(users); } @@ -382,7 +390,7 @@ private void InsertHelper(Func, T> helper) var users = new List(numberOfEntities); for (var i = 0; i < numberOfEntities; i++) - users.Add(new User { Name = "User " + i, Age = i }); + users.Add(new User { FullName = "User " + i, Age = i }); using (var connection = GetOpenConnection()) { @@ -390,7 +398,7 @@ private void InsertHelper(Func, T> helper) var total = connection.Insert(helper(users)); Assert.Equal(total, numberOfEntities); - users = connection.Query("select * from Users").ToList(); + users = connection.GetAll().ToList(); Assert.Equal(users.Count, numberOfEntities); } } @@ -420,7 +428,7 @@ private void UpdateHelper(Func, T> helper) var users = new List(numberOfEntities); for (var i = 0; i < numberOfEntities; i++) - users.Add(new User { Name = "User " + i, Age = i }); + users.Add(new User { FullName = "User " + i, Age = i }); using (var connection = GetOpenConnection()) { @@ -428,14 +436,14 @@ private void UpdateHelper(Func, T> helper) var total = connection.Insert(helper(users)); Assert.Equal(total, numberOfEntities); - users = connection.Query("select * from Users").ToList(); + users = connection.GetAll().ToList(); Assert.Equal(users.Count, numberOfEntities); foreach (var user in users) { - user.Name += " updated"; + user.FullName += " updated"; } connection.Update(helper(users)); - var name = connection.Query("select * from Users").First().Name; + var name = connection.GetAll().First().FullName; Assert.Contains("updated", name); } } @@ -465,7 +473,7 @@ private void DeleteHelper(Func, T> helper) var users = new List(numberOfEntities); for (var i = 0; i < numberOfEntities; i++) - users.Add(new User { Name = "User " + i, Age = i }); + users.Add(new User { FullName = "User " + i, Age = i }); using (var connection = GetOpenConnection()) { @@ -473,12 +481,12 @@ private void DeleteHelper(Func, T> helper) var total = connection.Insert(helper(users)); Assert.Equal(total, numberOfEntities); - users = connection.Query("select * from Users").ToList(); + users = connection.GetAll().ToList(); Assert.Equal(users.Count, numberOfEntities); var usersToDelete = users.Take(10).ToList(); connection.Delete(helper(usersToDelete)); - users = connection.Query("select * from Users").ToList(); + users = connection.GetAll().ToList(); Assert.Equal(users.Count, numberOfEntities - 10); } } @@ -492,30 +500,30 @@ public void InsertGetUpdate() Assert.Null(connection.Get(3)); //insert with computed attribute that should be ignored - connection.Insert(new Car { Name = "Volvo", Computed = "this property should be ignored" }); + connection.Insert(new Car { Model = "Volvo", Computed = "this property should be ignored" }); - var id = connection.Insert(new User { Name = "Adam", Age = 10 }); + var id = connection.Insert(new User { FullName = "Adam", Age = 10 }); //get a user with "isdirty" tracking var user = connection.Get(id); - Assert.Equal("Adam", user.Name); + Assert.Equal("Adam", user.FullName); Assert.False(connection.Update(user)); //returns false if not updated, based on tracking - user.Name = "Bob"; + user.FullName = "Bob"; Assert.True(connection.Update(user)); //returns true if updated, based on tracking user = connection.Get(id); - Assert.Equal("Bob", user.Name); + Assert.Equal("Bob", user.FullName); //get a user with no tracking var notrackedUser = connection.Get(id); - Assert.Equal("Bob", notrackedUser.Name); + Assert.Equal("Bob", notrackedUser.FullName); Assert.True(connection.Update(notrackedUser)); //returns true, even though user was not changed - notrackedUser.Name = "Cecil"; + notrackedUser.FullName = "Cecil"; Assert.True(connection.Update(notrackedUser)); - Assert.Equal("Cecil", connection.Get(id).Name); + Assert.Equal("Cecil", connection.Get(id).FullName); - Assert.Single(connection.Query("select * from Users")); + Assert.Single(connection.GetAll()); Assert.True(connection.Delete(user)); - Assert.Empty(connection.Query("select * from Users")); + Assert.Empty(connection.GetAll()); Assert.False(connection.Update(notrackedUser)); //returns false, user not found } @@ -557,7 +565,7 @@ public void InsertWithCustomDbType() [Fact] public void InsertWithCustomTableNameMapper() { - SqlMapperExtensions.TableNameMapper = type => + TypeExtensions.TableNameMapper = type => { switch (type.Name) { @@ -590,7 +598,7 @@ public void GetAll() var users = new List(numberOfEntities); for (var i = 0; i < numberOfEntities; i++) - users.Add(new User { Name = "User " + i, Age = i }); + users.Add(new User { FullName = "User " + i, Age = i }); using (var connection = GetOpenConnection()) { @@ -635,17 +643,18 @@ public void Transactions() { using (var connection = GetOpenConnection()) { - var id = connection.Insert(new Car { Name = "one car" }); //insert outside transaction + var id = connection.Insert(new Car { Model = "one car" }); //insert outside transaction var tran = connection.BeginTransaction(); var car = connection.Get(id, tran); - var orgName = car.Name; - car.Name = "Another car"; + var orgName = car.Model; + car.Model = "Another car"; connection.Update(car, tran); tran.Rollback(); car = connection.Get(id); //updates should have been rolled back - Assert.Equal(car.Name, orgName); + Assert.Equal(car.Model, orgName); + Assert.Equal(car.Computed, $"[{orgName}]"); } } #if TRANSCOPE @@ -672,7 +681,7 @@ public void InsertCheckKey() using (var connection = GetOpenConnection()) { Assert.Null(connection.Get(3)); - User user = new User { Name = "Adamb", Age = 10 }; + User user = new User { FullName = "Adamb", Age = 10 }; int id = (int)connection.Insert(user); Assert.Equal(user.Id, id); } @@ -687,7 +696,7 @@ public void BuilderSelectClause() var data = new List(100); for (int i = 0; i < 100; i++) { - var nU = new User { Age = rand.Next(70), Id = i, Name = Guid.NewGuid().ToString() }; + var nU = new User { Age = rand.Next(70), Id = i, FullName = Guid.NewGuid().ToString() }; data.Add(nU); nU.Id = (int)connection.Insert(nU); } @@ -699,12 +708,12 @@ public void BuilderSelectClause() builder.Select("Id"); var ids = connection.Query(justId.RawSql, justId.Parameters); - var users = connection.Query(all.RawSql, all.Parameters); + var users = connection.GetAll(); foreach (var u in data) { if (!ids.Any(i => u.Id == i)) throw new Exception("Missing ids in select"); - if (!users.Any(a => a.Id == u.Id && a.Name == u.Name && a.Age == u.Age)) throw new Exception("Missing users in select"); + if (!users.Any(a => a.Id == u.Id && a.FullName == u.FullName && a.Age == u.Age)) throw new Exception("Missing users in select"); } } } @@ -721,7 +730,7 @@ public void BuilderTemplateWithoutComposition() using (var connection = GetOpenConnection()) { connection.DeleteAll(); - connection.Insert(new User { Age = 5, Name = "Testy McTestington" }); + connection.Insert(new User { Age = 5, FullName = "Testy McTestington" }); if (connection.Query(template.RawSql, template.Parameters).Single() != 1) throw new Exception("Query failed"); @@ -746,8 +755,8 @@ public void DeleteAll() { using (var connection = GetOpenConnection()) { - var id1 = connection.Insert(new User { Name = "Alice", Age = 32 }); - var id2 = connection.Insert(new User { Name = "Bob", Age = 33 }); + var id1 = connection.Insert(new User { FullName = "Alice", Age = 32 }); + var id2 = connection.Insert(new User { FullName = "Bob", Age = 33 }); Assert.True(connection.DeleteAll()); Assert.Null(connection.Get(id1)); Assert.Null(connection.Get(id2)); diff --git a/tests/Dapper.Tests.Contrib/TestSuites.cs b/tests/Dapper.Tests.Contrib/TestSuites.cs index 55d4ef5c..2d952030 100644 --- a/tests/Dapper.Tests.Contrib/TestSuites.cs +++ b/tests/Dapper.Tests.Contrib/TestSuites.cs @@ -41,7 +41,7 @@ static SqlServerTestSuite() dropTable("Users"); connection.Execute("CREATE TABLE Users (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, Age int not null);"); dropTable("Automobiles"); - connection.Execute("CREATE TABLE Automobiles (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null);"); + connection.Execute("CREATE TABLE Automobiles (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, Computed AS Concat('[',Name,']'));"); dropTable("Results"); connection.Execute("CREATE TABLE Results (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, [Order] int not null);"); dropTable("ObjectX"); @@ -61,7 +61,7 @@ static SqlServerTestSuite() public class MySqlServerTestSuite : TestSuite { public static string ConnectionString { get; } = - GetConnectionString("MySqlConnectionString", "Server=localhost;Database=tests;Uid=test;Pwd=pass;UseAffectedRows=false;"); + GetConnectionString("MySqlConnectionString", "Server=localhost;Database=test;Uid=root;Pwd=root;UseAffectedRows=false;"); public override IDbConnection GetConnection() { @@ -87,7 +87,7 @@ static MySqlServerTestSuite() dropTable("Users"); connection.Execute("CREATE TABLE Users (Id int not null AUTO_INCREMENT PRIMARY KEY, Name nvarchar(100) not null, Age int not null);"); dropTable("Automobiles"); - connection.Execute("CREATE TABLE Automobiles (Id int not null AUTO_INCREMENT PRIMARY KEY, Name nvarchar(100) not null);"); + connection.Execute("CREATE TABLE Automobiles (Id int not null AUTO_INCREMENT PRIMARY KEY, Name nvarchar(100) not null, Computed nvarchar(150) GENERATED ALWAYS AS (Concat('[',Name,']')));"); dropTable("Results"); connection.Execute("CREATE TABLE Results (Id int not null AUTO_INCREMENT PRIMARY KEY, Name nvarchar(100) not null, `Order` int not null);"); dropTable("ObjectX"); @@ -130,7 +130,7 @@ static SQLiteTestSuite() connection.Execute("CREATE TABLE Stuff (TheId integer primary key autoincrement not null, Name nvarchar(100) not null, Created DateTime null) "); connection.Execute("CREATE TABLE People (Id integer primary key autoincrement not null, Name nvarchar(100) not null) "); connection.Execute("CREATE TABLE Users (Id integer primary key autoincrement not null, Name nvarchar(100) not null, Age int not null) "); - connection.Execute("CREATE TABLE Automobiles (Id integer primary key autoincrement not null, Name nvarchar(100) not null) "); + connection.Execute("CREATE TABLE Automobiles (Id integer primary key autoincrement not null, Name nvarchar(100) not null, Computed nvarchar(150) GENERATED ALWAYS AS ('[' || Name || ']')) "); connection.Execute("CREATE TABLE Results (Id integer primary key autoincrement not null, Name nvarchar(100) not null, [Order] int not null) "); connection.Execute("CREATE TABLE ObjectX (ObjectXId nvarchar(100) not null, Name nvarchar(100) not null) "); connection.Execute("CREATE TABLE ObjectY (ObjectYId integer not null, Name nvarchar(100) not null) ");