Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support ColumnAttribute for different column name #130

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dapper.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 13 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------

Expand Down
55 changes: 55 additions & 0 deletions src/Dapper.Contrib/PropertyInfoExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;

namespace Dapper.Contrib.Extensions
{
/// <summary>
/// Map <see cref="PropertyInfo"/> to database column extensions
/// </summary>
public static class PropertyInfoExtensions
{
private static readonly ConcurrentDictionary<PropertyInfo, string> PropertyColumnName = new ConcurrentDictionary<PropertyInfo, string>();

/// <summary>
/// The function to get a column name from a given <see cref="PropertyInfo"/>
/// </summary>
/// <param name="property">The <see cref="PropertyInfo"/> to get a column name for.</param>
public delegate string ColumnNameMapperDelegate(PropertyInfo property);

/// <summary>
/// Specify a custom column name mapper based on the POCO type name
/// </summary>
#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

/// <summary>
/// Get database column name from <see cref="PropertyInfo"/>
/// </summary>
/// <param name="property">The <see cref="PropertyInfo"/> to get a column name for.</param>
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<ColumnAttribute>(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;
}
}
}
75 changes: 57 additions & 18 deletions src/Dapper.Contrib/SqlMapperExtensions.Async.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,34 @@ public static partial class SqlMapperExtensions
public static async Task<T> GetAsync<T>(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<T>(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();
Expand All @@ -47,7 +68,7 @@ public static async Task<T> GetAsync<T>(this IDbConnection connection, dynamic i

var obj = ProxyGenerator.GetInterfaceProxy<T>();

foreach (var property in TypePropertiesCache(type))
foreach (var property in properties)
{
var val = res[property.Name];
if (val == null) continue;
Expand Down Expand Up @@ -82,14 +103,32 @@ public static Task<IEnumerable<T>> GetAllAsync<T>(this IDbConnection connection,
{
var type = typeof(T);
var cacheType = typeof(List<T>);
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<T>(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)
Expand All @@ -108,7 +147,7 @@ private static async Task<IEnumerable<T>> GetAllAsyncImpl<T>(IDbConnection conne
var obj = ProxyGenerator.GetInterfaceProxy<T>();
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<>))
{
Expand Down Expand Up @@ -162,7 +201,7 @@ public static Task<int> InsertAsync<T>(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();
Expand All @@ -172,7 +211,7 @@ public static Task<int> InsertAsync<T>(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(", ");
}
Expand Down Expand Up @@ -237,7 +276,7 @@ public static async Task<bool> UpdateAsync<T>(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);
Expand All @@ -252,15 +291,15 @@ public static async Task<bool> UpdateAsync<T>(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(", ");
}
sb.Append(" where ");
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 ");
}
Expand Down Expand Up @@ -306,7 +345,7 @@ public static async Task<bool> DeleteAsync<T>(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();
Expand All @@ -317,7 +356,7 @@ public static async Task<bool> DeleteAsync<T>(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 ");
}
Expand All @@ -336,7 +375,7 @@ public static async Task<bool> DeleteAsync<T>(this IDbConnection connection, T e
public static async Task<bool> DeleteAllAsync<T>(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;
}
Expand Down
Loading