Skip to content

Instantly share code, notes, and snippets.

@kalebpederson
Last active July 20, 2025 23:04
Show Gist options
  • Select an option

  • Save kalebpederson/5460509 to your computer and use it in GitHub Desktop.

Select an option

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);
}
}
}
@BeBa-pL

BeBa-pL commented Aug 23, 2017

Copy link
Copy Markdown

Trying to use it with the current version, this is missing the ITypeMap method FindExplicitConstructor

@rsequeirar

rsequeirar commented Jan 24, 2018

Copy link
Copy Markdown

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;
    }

@washingtonpraxedes

washingtonpraxedes commented Mar 28, 2018

Copy link
Copy Markdown

Does this map work for PK attributes ?

@tingwei628

tingwei628 commented Jan 16, 2020

Copy link
Copy Markdown

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)
                               )
                       ),

@auroraegan

auroraegan commented Mar 29, 2020

Copy link
Copy Markdown

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

@mishrsud

Copy link
Copy Markdown

@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;

@dannylloyd

Copy link
Copy Markdown

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);
}

@dannylloyd

dannylloyd commented Aug 4, 2021

Copy link
Copy Markdown

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");
}

@Anda001

Anda001 commented Jul 7, 2022

Copy link
Copy Markdown

I can use it to 'query',but can't use to 'update' or 'insert'.

@RamseJones

Copy link
Copy Markdown

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.

@AlaskanDruid

Copy link
Copy Markdown

Anyone get this working with Dapper 2.1.66 yet?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment