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);
}
}
}
@ronbuchanan
Copy link
Copy Markdown

This didn't work for me. In the end I had to put the column names in double-quotes (") matching the properties of the object I wanted mapped. Not sure if I was using the wrong ColumnAttribute class (System.ComponentModel.DataAnnotations.Schema) or not.

@Crisfole
Copy link
Copy Markdown

@ronbuchanan The attribute is in the above code. Look for "ColumnMappingAttribute". It's a custom, very simple, one.

@LosManos
Copy link
Copy Markdown

Example of class is

public class Customer
{
    public int CustomerID { get; set; }

    [Column(Name = "Customer_Name")]
    public string Name{ get; set; }
}

@jessiepinkman
Copy link
Copy Markdown

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

@BeBa-pL
Copy link
Copy Markdown

BeBa-pL commented Aug 23, 2017

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

@rsequeirar
Copy link
Copy Markdown

rsequeirar commented Jan 24, 2018

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
Copy link
Copy Markdown

washingtonpraxedes commented Mar 28, 2018

Does this map work for PK attributes ?

@tingwei628
Copy link
Copy Markdown

tingwei628 commented Jan 16, 2020

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
Copy link
Copy Markdown

auroraegan commented Mar 29, 2020

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
Copy link
Copy Markdown

dannylloyd commented Aug 4, 2021

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
Copy link
Copy Markdown

Anda001 commented Jul 7, 2022

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