-
-
Save kalebpederson/5460509 to your computer and use it in GitHub Desktop.
namespace YourNamespace | |
{ | |
/// <summary> | |
/// Uses the Name value of the <see cref="ColumnAttribute"/> specified to determine | |
/// the association between the name of the column in the query results and the member to | |
/// which it will be extracted. If no column mapping is present all members are mapped as | |
/// usual. | |
/// </summary> | |
/// <typeparam name="T">The type of the object that this association between the mapper applies to.</typeparam> | |
public class ColumnAttributeTypeMapper<T> : FallbackTypeMapper | |
{ | |
public ColumnAttributeTypeMapper() | |
: base(new SqlMapper.ITypeMap[] | |
{ | |
new CustomPropertyTypeMap( | |
typeof(T), | |
(type, columnName) => | |
type.GetProperties().FirstOrDefault(prop => | |
prop.GetCustomAttributes(false) | |
.OfType<ColumnAttribute>() | |
.Any(attr => attr.Name == columnName) | |
) | |
), | |
new DefaultTypeMap(typeof(T)) | |
}) | |
{ | |
} | |
} | |
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] | |
public class ColumnAttribute : Attribute | |
{ | |
public string Name { get; set; } | |
} | |
public class FallbackTypeMapper : SqlMapper.ITypeMap | |
{ | |
private readonly IEnumerable<SqlMapper.ITypeMap> _mappers; | |
public FallbackTypeMapper(IEnumerable<SqlMapper.ITypeMap> mappers) | |
{ | |
_mappers = mappers; | |
} | |
public ConstructorInfo FindConstructor(string[] names, Type[] types) | |
{ | |
foreach (var mapper in _mappers) | |
{ | |
try | |
{ | |
ConstructorInfo result = mapper.FindConstructor(names, types); | |
if (result != null) | |
{ | |
return result; | |
} | |
} | |
catch (NotImplementedException) | |
{ | |
} | |
} | |
return null; | |
} | |
public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName) | |
{ | |
foreach (var mapper in _mappers) | |
{ | |
try | |
{ | |
var result = mapper.GetConstructorParameter(constructor, columnName); | |
if (result != null) | |
{ | |
return result; | |
} | |
} | |
catch (NotImplementedException) | |
{ | |
} | |
} | |
return null; | |
} | |
public SqlMapper.IMemberMap GetMember(string columnName) | |
{ | |
foreach (var mapper in _mappers) | |
{ | |
try | |
{ | |
var result = mapper.GetMember(columnName); | |
if (result != null) | |
{ | |
return result; | |
} | |
} | |
catch (NotImplementedException) | |
{ | |
} | |
} | |
return null; | |
} | |
public ConstructorInfo FindExplicitConstructor() | |
{ | |
return _mappers | |
.Select(mapper => mapper.FindExplicitConstructor()) | |
.FirstOrDefault(result => result != null); | |
} | |
} | |
} |
It worked in Dapper 1.60.6.
NOTE : If you want to map properties of the model which has already Column
attribute in Entity Framework, you don't need to define another custom ColumnAttribute
Just replace ColumnAttribute
with System.ComponentModel.DataAnnotations.Schema.ColumnAttribute
, like this
new CustomPropertyTypeMap(
typeof(T),
(type, columnName) =>
type.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(false)
.OfType<System.ComponentModel.DataAnnotations.Schema.ColumnAttribute>()
.Any(attr => attr.Name == columnName)
)
),
Sorry, currently, I read and try to understand for use this library. But, I don't know how to apply it in .net core 2.1?
` public async Task<TEntity> RawQuerySingleAsync(string query)
{
using (var connection = new SqlConnection(ServerConnectionString))
{
var k = new ColumnAttributeTypeMapper<TEntity>(); //????
var result = await connection.QueryFirstOrDefaultAsync<TEntity>(query);
return result;
}
}`
my simple has: select * from Users and have Column User_Id, It's must bi mapped with UserId?
currently, my dapper version: 2.0.30
@auroraegan your use case is supported out of the box by Dapper. Use this line in your application startup / entry point:
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
I have this code in my Global.asax.cs, is it possible to generalize this so that it runs against all classes? Or do I have to SqlMapper.SetTypeMap
for every class that uses the column attribute?
protected void Application_Start()
{
var mapper = (SqlMapper.ITypeMap)Activator
.CreateInstance(typeof(ColumnAttributeTypeMapper<>)
.MakeGenericType(typeof(User)));
SqlMapper.SetTypeMap(typeof(User), mapper);
}
Follow-up, I found a solution that does exactly what I was looking for. https://stackoverflow.com/a/20969521/618186.
I will say that I don't know if it's the best approach because it's reflecting on the entire assembly, but since it's only being done once per application start it should be terrible. That's the trade-off of making the coding easier.
Here is the important part that you need. Add this to the ColumnAttributeTypeMapper.cs that @kalebpederson wrote.
public static class TypeMapper
{
public static void Initialize(string @namespace)
{
var types = from assem in AppDomain.CurrentDomain.GetAssemblies().ToList()
from type in assem.GetTypes()
where type.IsClass && type.Namespace == @namespace
select type;
types.ToList().ForEach(type =>
{
var mapper = (SqlMapper.ITypeMap)Activator
.CreateInstance(typeof(ColumnAttributeTypeMapper<>)
.MakeGenericType(type));
SqlMapper.SetTypeMap(type, mapper);
});
}
}
Put this in your Global.asax.cs
protected void Application_Start()
{
TypeMapper.Initialize("YourNamespace.Entities");
}
I can use it to 'query',but can't use to 'update' or 'insert'.
I followed everything here, created all classes of ColumnAttributeTypeMapper.
Add on Startup =>
services.AddAutoMapper(System.Reflection.Assembly.GetExecutingAssembly());
TypeMapper.Initialize("Dapper.Core.Entities");
My Class
public class Product
{
[Column("Id_Prod")]
public int Id { get; set; }
}
Repository
public async Task<IReadOnlyList> GetAllAsync()
{
var sql = "SELECT * FROM Products";
using (var connection = new SqlConnection(configuration.GetConnectionString("DefaultConnection")))
{
connection.Open();
var result = await connection.QueryAsync(sql);
return result.ToList();
}
}
Dapper 1.50 and AutoMapper 8.0
I feel it's missing one little detail, but i'm searching for days.
Does this map work for PK attributes ?