Я использую в своем учебном проекте 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()
наподобие того как вы сделали со списком свойств - выполняться он будет намного быстрее чем через рефлексию.
Виртуальный выделенный сервер (VDS) становится отличным выбором
Помогите решить проблему, пожалуйстаВ unity работаю совсем не долго, как решать подобное?
Столкнулся со следующей проблемойИмеется некий метод,записывающий сущность в бд, примерно такой:
У меня есть около десяти тысяч строчек в таблице следующего формата: