- Регистрация
- 9 Май 2015
- Сообщения
- 1,483
- Баллы
- 155

I am using postgres sql as database. I have mapped my classes earlier using Entity Framework and used the Column[("name")] attribute. Now that I have switched to Dapper, it seemed Dapper does not read any of the column attributes that I have used in my DTO/POCO classes. Ah! That's going to be a problem.
So my class kinda looks like this
public class Role
{
[Column("role_id")]
public long RoleID { get; set; }
[Column("role_name")]
public string RoleName { get; set; }
}
Now this is going to be an issue. I could rewrite my queries like this ---
const string sql =
@"SELECT role_id AS RoleID,
role_name AS RoleName
FROM public.role
WHERE role_id = @id;";
Now this is a hassle that I dont want to deal with. It seems Dapper.FluentMap has a way to map these column names to properties but that does not happen thorough attributes. Here is how to do that ---
public class RoleMap : EntityMap<Role>
{
public RoleMap()
{
Map(r => r.RoleID).ToColumn("role_id");
Map(r => r.RoleName).ToColumn("role_name");
}
}
and then register the mapping class like this
FluentMapper.Initialize(c => c.AddMap(new RoleMap()));
Now I ain't going to write one class for each entities that I have created. So I created a generic class called the ColumnMapper and that takes care of the mapping if your DTO/POCO class uses System.ComponentModel.DataAnnotations.Schema and maps using the ColumnAttribute. Which I already do. So here is the class that does that.
public class ColumnMapper < T >: EntityMap < T > where T: class {
public ColumnMapper() {
// Get all public instance properties of the entity type T.
PropertyInfo[] infos = typeof (T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach(var propertyInfo in infos) {
// Check if the property has a [Column] attribute.
var columnAttr = propertyInfo.GetCustomAttribute < System.ComponentModel.DataAnnotations.Schema.ColumnAttribute > ();
if (columnAttr != null) {
// --- Reflection to call Map(lambda).ToColumn(columnName) ---
var parameter = Expression.Parameter(typeof (T), "x");
var property = Expression.Property(parameter, propertyInfo);
var convert = Expression.Convert(property, typeof (object));
var lambda = Expression.Lambda < Func < T,
object >> (convert, parameter);
var mapMethod = typeof (EntityMap < T > )
.GetMethod("Map", BindingFlags.Instance | BindingFlags.NonPublic);
if (mapMethod == null) {
throw new InvalidOperationException("Could not find the 'Map' method via reflection. The Dapper.FluentMap API may have changed.");
}
var propertyMap = mapMethod.Invoke(this, new object[] {
lambda
});
var toColumnMethod = propertyMap.GetType()
.GetMethod("ToColumn", new [] {
typeof (string), typeof (bool)
});
if (toColumnMethod != null) {
toColumnMethod.Invoke(propertyMap, new object[] {
columnAttr.Name, false
});
}
}
}
}
Then I can call this generic mapper class like this
FluentMapper.Initialize(cfg =>
{
cfg.AddMap(new ColumnMapper<User>());
cfg.AddMap(new ColumnMapper<Role>());
cfg.AddMap(new ColumnMapper<Permission>());
cfg.AddMap(new ColumnMapper<RolePermissionMap>());
cfg.AddMap(new ColumnMapper<RoleUserMap>());
cfg.AddMap(new ColumnMapper<ServiceInfo>());
cfg.AddMap(new ColumnMapper<ServiceInstance>());
cfg.AddMap(new ColumnMapper<Queue>());
cfg.AddMap(new ColumnMapper<Token>());
});
Walla! I am done!
Since this is called only once during startup it has no performance issues either. Let me know what you guys think.
Источник: