DTO到实体映射工具

我有一个实体类Person及其对应的DTO类PersonDto

 public class Person: Entity { public virtual string Name { get; set; } public virtual string Phone { get; set; } public virtual string Email { get; set; } public virtual Sex Sex { get; set; } public virtual Position Position { get; set; } public virtual Division Division { get; set; } public virtual Organization Organization { get; set; } } public class PersonDto: Dto { public string Name { get; set; } public string Phone { get; set; } public string Email { get; set; } public Guid SexId { get; set; } public Guid PositionId { get; set; } public Guid DivisionId { get; set; } public Guid OrganizationId { get; set; } } 

收到DTO对象后,我必须将其转换为person实体。 现在我完全手动完成。 代码看起来像这样。

 public class PersonEntityMapper: IEntityMapper { private IRepository _personRepository; private IRepository _sexRepository; private IRepository _positionRepository; private IRepository _divisionRepository; private IRepository _organizationRepository; public PersonEntityMapper(IRepository personRepository, IRepository sexRepository, IRepository positionRepository, IRepository divisionRepository, IRepository organizationRepository) { ... // Assigning repositories } Person Map(PersonDto dto) { Person person = CreateOrLoadPerson(dto); person.Name = dto.Name; person.Phone = dto.Phone; person.Email = dto.Email; person.Sex = _sexRepository.LoadById(dto.SexId); person.Position = _positionRepository.LoadById(dto.PositionId); person.Division = _divisionRepository.LoadById(dto.DivisionId); person.Organization = _organizationRepository.LoadById(dto.OrganizationId); return person; } } 

代码实际上是微不足道的。 但随着实体数量的增加,映射器类的数量也在增加。 结果是许多类似的代码。 另一个问题是,当存在模式关联时,我必须为其他存储库添加构造函数参数。 我尝试注入某种类型的存储库工厂,但它闻到了一个鲜为人知的Service Locator所以我恢复了原来的解决方案。

这些映射器的unit testing还导致许多类似的测试方法。

有了这一切,我想知道是否存在可以减少手动编写代码量并使unit testing更容易的解决方案。

提前致谢。

UPDATE

我用Value Injecter完成了任务但后来我意识到我可以安全地删除它,其余的仍然可以工作。 这是最终的解决方案。

 public abstract class BaseEntityMapper : IEntityMapper where TEntity : Entity, new() where TDto : BaseDto { private readonly IRepositoryFactory _repositoryFactory; protected BaseEntityMapper(IRepositoryFactory repositoryFactory) { _repositoryFactory = repositoryFactory; } public TEntity Map(TDto dto) { TEntity entity = CreateOrLoadEntity(dto.State, dto.Id); MapPrimitiveProperties(entity, dto); MapNonPrimitiveProperties(entity, dto); return entity; } protected abstract void MapNonPrimitiveProperties(TEntity entity, TDto dto); protected void MapPrimitiveProperties(TTarget target, TSource source, string prefix = "") { var targetProperties = target.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name); var sourceProperties = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name); foreach (var targetProperty in targetProperties) { foreach (var sourceProperty in sourceProperties) { if (sourceProperty.Name != string.Format("{0}{1}", prefix, targetProperty.Name)) continue; targetProperty.SetValue(target, sourceProperty.GetValue(source, null), null); break; } } } protected void MapAssociation(TTarget target, Expression<Func> expression, Guid id) where T : Entity { var repository = _repositoryFactory.Create(); var propertyInfo = (PropertyInfo)((MemberExpression)expression.Body).Member; propertyInfo.SetValue(target, repository.LoadById(id), null); } private TEntity CreateOrLoadEntity(DtoState dtoState, Guid entityId) { if (dtoState == DtoState.Created) return new TEntity(); if (dtoState == DtoState.Updated) { return _repositoryFactory.Create().LoadById(entityId); } throw new BusinessException("Unknown DTO state"); } } 

使用从BaseEntityMapper派生的具体类来执行每个实体的映射。 Person实体的那个看起来像这样。

 public class PersonEntityMapper: BaseEntityMapper { public PersonEntityMapper(IRepositoryFactory repositoryFactory) : base(repositoryFactory) {} protected override void MapNonPrimitiveProperties(Person entity, PersonDto dto) { MapAssociation(entity, () => entity.Sex, dto.SexId); MapAssociation(entity, () => entity.Position, dto.PositionId); MapAssociation(entity, () => entity.Organization, dto.OrganizationId); MapAssociation(entity, () => entity.Division, dto.DivisionId); } } 

明确调用MapAssociation可以防止将来的属性重命名。

您可以查看两个最常用的Object-Object映射器:

AutoMapper

AutoMapper是一个简单的小型库,用于解决一个看似复杂的问题 – 摆脱将一个对象映射到另一个对象的代码。 这种类型的代码是相当沉闷和无聊的写,所以为什么不发明一个工具来为我们做?

价值注入

ValueInjecter允许您定义自己的基于约定的匹配算法(ValueInjections),以便将源值匹配(注入)到目标值。

关于SO的比较文章: AutoMapper与ValueInjecter

您可以使用GeDA将任何实体映射到DTO对象,它带有注释或DSL支持。

http://inspire-software.com/confluence/display/GeDA/FAQ

维基上只有基本的例子,但源代码的jUnits充满了有用的例子

您可以手动或通过maven依赖从sourceforge或Google代码获取它

详细信息如下: http : //inspire-software.com/confluence/display/GeDA/GeDA+-+Generic+DTO+Assembler