DTO to Entity Mapping Tool

I have a Person entity class and its corresponding DTO PersonDto class.

 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; } } 

After receiving the DTO object, I have to convert it to a person object. Now I do it completely manually. The code is as follows.

 public class PersonEntityMapper: IEntityMapper<Person, PersonDto> { private IRepository<Person> _personRepository; private IRepository<Sex> _sexRepository; private IRepository<Position> _positionRepository; private IRepository<Division> _divisionRepository; private IRepository<Organization> _organizationRepository; public PersonEntityMapper(IRepository<Person> personRepository, IRepository<Sex> sexRepository, IRepository<Position> positionRepository, IRepository<Division> divisionRepository, IRepository<Organization> 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; } } 

The code is actually trivial. But as the number of entities grows, the number of matching classes. The result is a lot of similar code. Another problem is that when there are mode associations, I have to add constructor parameters for additional repositories. Instead, I tried to introduce some kind of factory repository, but it felt a poorly known Service Locator , so I went back to the original solution.

Unit testing of these cards also leads to a number of similar testing methods.

With all of this in mind, I wonder if there is a solution that can reduce the amount of hand-written code and facilitate device testing.

Thanks in advance.

UPDATE

I completed the task with Value Injecter , but then I realized that I can safely delete it, and the rest will still work. Here is the resulting solution.

 public abstract class BaseEntityMapper<TEntity, TDto> : IEntityMapper<TEntity, TDto> 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, TSource>(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, T>(TTarget target, Expression<Func<T>> expression, Guid id) where T : Entity { var repository = _repositoryFactory.Create<T>(); 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<TEntity>().LoadById(entityId); } throw new BusinessException("Unknown DTO state"); } } 

Each object is BaseEntityMapper using a specific class derived from BaseEntityMapper . The object for Person objects is as follows.

 public class PersonEntityMapper: BaseEntityMapper<Person, PersonDto> { 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); } } 

An explicit call to MapAssociation protects against renaming future properties.

+8
c # mapping dto
source share
2 answers

You can take a look at the two most used Object-Objects:

AutoMapper

AutoMapper is a simple small library created for a deceptive complex problem - getting rid of code that maps one object to another. This type of code is pretty boring and boring to write, so why not come up with a tool for this?

Value Injector

ValueInjecter allows you to define your own consistency based on agreement ValueInjections algorithms in order to match (enter) the source of the value for the destination values.

There is a comparative article on SO: AutoMapper vs ValueInjecter

+6
source share

You can use GeDA to map any object to a DTO object; it comes with annotations or DSL support.

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

There are only basic examples in the wiki, but the source code jUnits are full of useful examples

You can get it from source or Google code manually or through maven dependency

Details here: http://inspire-software.com/confluence/display/GeDA/GeDA+-+Generic+DTO+Assembler

+1
source share

All Articles