• Что бы вступить в ряды "Принятый кодер" Вам нужно:
    Написать 10 полезных сообщений или тем и Получить 10 симпатий.
    Для того кто не хочет терять время,может пожертвовать средства для поддержки сервеса, и вступить в ряды VIP на месяц, дополнительная информация в лс.

  • Пользаватели которые будут спамить, уходят в бан без предупреждения. Спам сообщения определяется администрацией и модератором.

  • Гость, Что бы Вы хотели увидеть на нашем Форуме? Изложить свои идеи и пожелания по улучшению форума Вы можете поделиться с нами здесь. ----> Перейдите сюда
  • Все пользователи не прошедшие проверку электронной почты будут заблокированы. Все вопросы с разблокировкой обращайтесь по адресу электронной почте : info@guardianelinks.com . Не пришло сообщение о проверке или о сбросе также сообщите нам.

Smarter Generic Mapping in Dapper.FluentMap while using DataAnnotations

Sascha Оффлайн

Sascha

Заместитель Администратора
Команда форума
Администратор
Регистрация
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.



Источник:

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

 
Вверх Снизу