Многие знают про отличную библиотеку Automapper. С преобразованием Entity -> Dto проблем обычно не возникает. Но как обрабатывать обратный маппинг в случае, когда в API приходит корень агрегации? Хорошо, если read и write — контексты разделены и писать можно из Dto. Чаще, однако, нужно выбрать соответствующие сущности из ORM по Id и сохранить агрегат целиком. Занятие это муторное, однако зачастую поддающееся автоматизации.
Объявляем такой TypeConverter:
public class EntityTypeConverter<TDto, TEntity> : ITypeConverter<TDto, TEntity>
where TEntity: PersistentObject, new()
{
public TEntity Convert(ResolutionContext context)
{
// Забираем из контейнера DbContext
// Да, ServiceLocator это плохо, но о том как прикрутить IOC вы сможете и сами прочесть по ссылке
// http://stackoverflow.com/questions/4204664/automapper-together-with-dependency-injection
var dc = ApplicationContext.Current.Container.Resolve<IDbContext>();
var sourceId = (context.SourceValue as IEntity)?.Id;
var dest = context.DestinationValue as TEntity
?? (sourceId.HasValue && sourceId.Value != 0 ? dc.Get<TEntity>(sourceId.Value) : new TEntity());
// Да, reflection, да медленно и может привести к ошибкам в рантайме.
// Можете написать Expression Trees, скомпилировать и закешировать для производительности
// И анализатор для проверки корректности Dto на этапе компиляции
var sp = typeof(TDto)
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.CanRead && x.CanWrite)
.ToDictionary(x => x.Name.ToUpper(), x => x);
var dp = typeof(TEntity)
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.CanRead && x.CanWrite)
.ToArray();
// проходимся по всем свойствам целевого объекта
foreach (var propertyInfo in dp)
{
var key = propertyInfo.PropertyType.InheritsOrImplements(typeof(PersistentObject))
? propertyInfo.Name.ToUpper() + "ID"
: propertyInfo.Name.ToUpper();
if (sp.ContainsKey(key))
{
// маппим один к одному примитивы, связанные сущности тащим из контекста
propertyInfo.SetValue(dest, key.EndsWith("ID")
&& propertyInfo.PropertyType.InheritsOrImplements(typeof(PersistentObject))
? dc.Get(propertyInfo.PropertyType, sp[key].GetValue(context.SourceValue))
: sp[key].GetValue(context.SourceValue));
}
}
return dest;
}
}
И создаем маппинг:
AutoMapper.Mapper
.CreateMap<TDto, TEntity>()
.ConvertUsing<EntityTypeConverter<TDto, TEntity>>();
Автор: marshinov