首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >DTO到实体映射工具

DTO到实体映射工具
EN

Stack Overflow用户
提问于 2012-03-19 20:18:10
回答 2查看 16.2K关注 0票数 8

我有一个实体类Person和它对应的DTO类PersonDto

代码语言:javascript
复制
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实体。现在我完全手动完成。代码如下所示。

代码语言:javascript
复制
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;
  }
}

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

对这些映射器进行单元测试还会产生许多类似的测试方法。

说了这么多,我想知道是否有一种解决方案可以减少手动编写的代码量,并使单元测试更容易。

提前谢谢。

更新

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

代码语言:javascript
复制
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");
        }
    }  

使用从BaseEntityMapper派生的具体类执行每个实体的映射。Person实体的示例如下所示。

代码语言:javascript
复制
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);
        }
    }

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

EN

回答 2

Stack Overflow用户

发布于 2012-03-19 20:24:59

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

AutoMapper

AutoMapper是一个简单的小型库,旨在解决一个看似复杂的问题--去掉将一个对象映射到另一个对象的代码。这种类型的代码写起来相当枯燥乏味,所以为什么不为我们发明一个工具来完成它呢?

Value Injecter

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

在SO上有一篇比较文章:AutoMapper vs ValueInjecter

票数 6
EN

Stack Overflow用户

发布于 2013-02-06 08:27:32

您可以使用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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9770041

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档