Многие знают про отличную библиотеку AutoMapper. С преобразованием Entity -> Dto проблем обычно не возникает. Но как обрабатывать обратный маппинг в случае, когда в API приходит корень агрегации? Хорошо, если read и write — контексты разделены и писать можно из Dto. Чаще, однако, нужно выбрать соответствующие сущности из ORM по Id и сохранить агрегат целиком. Занятие это муторное, однако зачастую поддающееся автоматизации.
Объявляем такой TypeConverter:
И создаем маппинг:
Объявляем такой 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>>();