-
-
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); | |
} | |
} | |
} |
Hey Guys,
Did anything change recently on the dapper side in relation to this. Because I am unable to get this thing to work on latest Dapper 1.42.
I added the above class into my code and then added the below attribute..
Also, I added below two lines in my main function.
var mapper = (SqlMapper.ITypeMap)Activator
.CreateInstance(typeof(ColumnAttributeTypeMapper<>)
.MakeGenericType(typeof(Customer)));
SqlMapper.SetTypeMap(typeof(Customer), mapper);
.And when I call the stored procedure which returns the data, I am not seeing the first attribute being populated. Any help would be really appreciated
[Column(Name = "Customer_NM")]
public string CustomerName{ get; set; }
public string Phone{ get; set; }
Trying to use it with the current version, this is missing the ITypeMap method FindExplicitConstructor
This is working for me in Dapper 1.50.2, but you have to implement the method FindExplicitConstructor like this, @ITCBB:
{
foreach (var mapper in _mappers)
{
try
{
ConstructorInfo result = mapper.FindExplicitConstructor();
if (result != null)
{
return result;
}
}
catch (NotImplementedException)
{
}
}
return null;
}
Does this map work for PK attributes ?
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.
Example of class is