Я использую в своем учебном проекте AutoMapper, но мне он кажется неудобным инструментом. Я сделал свою функцию для конвертации данных из ViewModel в Model и обратно. Переносятся только свойства.
public static class Mapper
{
private static readonly Dictionary<Type, Dictionary<string, PropertyInfo>> _propertiesDictionaries
= new Dictionary<Type, Dictionary<string, PropertyInfo>>();
public static T Map<T>(object obj) where T : new()
{
var objProperties = GetProperties(obj.GetType());
var resultPropertyes = GetProperties(typeof(T));
var result = new T();
foreach (var resultProperty in resultPropertyes)
{
if (objProperties.TryGetValue(resultProperty.Key, out var objProperty))
{
resultProperty.Value.SetValue(result, objProperty.GetValue(obj));
}
}
return result;
}
private static Dictionary<string, PropertyInfo> GetProperties(Type objType)
{
if (!_propertiesDictionaries.TryGetValue(objType, out var propertiesInfoDictionary))
{
var infos = objType.GetProperties();
propertiesInfoDictionary = new Dictionary<string, PropertyInfo>();
foreach (var propertyInfo in infos)
{
propertiesInfoDictionary.Add(propertyInfo.Name, propertyInfo);
}
_propertiesDictionaries.Add(objType, propertiesInfoDictionary);
}
return propertiesInfoDictionary;
}
}
Можно ли сделать мою функцию лучше/быстрее? Какие есть аналоги у AutoMapper?
Update 1 Версия с использованием Expression:
public static class MapperExpression
{
private static readonly Dictionary<Type, Dictionary<string, PropertyInfo>> _propertiesDictionaries
= new Dictionary<Type, Dictionary<string, PropertyInfo>>();
private static readonly Dictionary<Type, Dictionary<Type, Func<object, object>>> _mappersDictionaries = new
Dictionary<Type, Dictionary<Type, Func<object, object>>>();
public static T Map<T>(object source) where T : new()
{
var targetType = typeof(T);
var sourceType = source.GetType();
if (_mappersDictionaries.TryGetValue(targetType, out var targetTypeMappers))
{
if (targetTypeMappers.TryGetValue(sourceType, out var mapper))
{
return (T)mapper.Invoke(source);
}
else
{
mapper = CreateMapper<T>(source.GetType());
targetTypeMappers.Add(sourceType, mapper);
return (T)mapper.Invoke(source);
}
}
else
{
targetTypeMappers = new Dictionary<Type, Func<object, object>>();
var mapper = CreateMapper<T>(source.GetType());
targetTypeMappers.Add(source.GetType(), mapper);
_mappersDictionaries.Add(targetType, targetTypeMappers);
return (T)mapper.Invoke(source);
}
}
private static Func<object, object> CreateMapper<T>(Type sourceType) where T : new()
{
var sourceProperties = GetProperties(sourceType);
var targetPropertyes = GetProperties(typeof(T));
var paramExpr = Expression.Parameter(typeof(object));
var sourceExpr = Expression.Convert(paramExpr, sourceType);
var bindings = new List<MemberBinding>();
foreach (var targetProperty in targetPropertyes)
{
if (sourceProperties.TryGetValue(targetProperty.Key, out var sourceProperty))
{
bindings.Add(Expression.Bind(targetProperty.Value, Expression.Property(sourceExpr, sourceProperty)));
}
}
var resultExpr = Expression.MemberInit(Expression.New(typeof(T)), bindings);
var mapperExpr = Expression.Lambda<Func<object, object>>(resultExpr, paramExpr);
return mapperExpr.Compile();
}
private static Dictionary<string, PropertyInfo> GetProperties(Type objType)
{
if (!_propertiesDictionaries.TryGetValue(objType, out var propertiesInfoDictionary))
{
var infos = objType.GetProperties();
propertiesInfoDictionary = new Dictionary<string, PropertyInfo>();
foreach (var propertyInfo in infos)
{
propertiesInfoDictionary.Add(propertyInfo.Name, propertyInfo);
}
_propertiesDictionaries.Add(objType, propertiesInfoDictionary);
}
return propertiesInfoDictionary;
}
}
Сравнение первой версии со второй (.net core 2.0, 2000000 конвертаций):
Да, можно. Вместо прямой работы со свойствами составьте выражение:
var paramExpr = Expression.Parameter(typeof(object));
var sourceExpr = Expression.Convert(paramExpr, obj.GetType());
var bindings = new List<MemberBinding>();
foreach (var resultProperty in resultPropertyes) {
if (ваша логика) {
bindings.Add(Expression.Bind(resultProperty, источник));
}
}
var resultExpr = Expression.MemberInit(Expression.New(typeof(T)), bindings);
var mapperExpr = Expression.Lambda<Func<object,T>>(resultExpr, paramExpr);
var mapper = mapperExpr.Compile();
return mapper(obj);
В приведенном мной виду работать будет медленнее чем через рефлексию. Но если закешировать делегат mapper
для пары ключей typeof(T), obj.GetType()
наподобие того как вы сделали со списком свойств - выполняться он будет намного быстрее чем через рефлексию.
Оборудование для ресторана: новинки профессиональной кухонной техники
Частный дом престарелых в Киеве: комфорт, забота и профессиональный уход
Помогите решить проблему, пожалуйстаВ unity работаю совсем не долго, как решать подобное?
Столкнулся со следующей проблемойИмеется некий метод,записывающий сущность в бд, примерно такой:
У меня есть около десяти тысяч строчек в таблице следующего формата: