将父实体和子实体映射到DTO时的循环依赖关系

时间:2013-10-18 00:53:10

标签: asp.net-mvc-4 entity-framework-5 repository-pattern unit-of-work data-transfer-objects

我刚刚使用this问题中显示的jevelez方法在一个通用存储库中使用显式加载子实体及其父级实现。

但是现在我正在尝试将子实体及其父实体映射到DTO,以便将它们发送到我的UI层。

以下是我的地图制作者目前的样子:

映射界面:

public interface IMappingService<TEntity, TDto>
    where TEntity : class 
    where TDto : class
{
    TDto EntityToDto(TEntity entity);
    TEntity DtoToEntity(TDto dto);
    IEnumerable<TDto> EntitiesToDtos(IList<TEntity> entities);
    IEnumerable<TEntity> DtosToEntities(IList<TDto> dtos);
}

实现接口的抽象基方法:

public abstract class MapperBase<TEntity, TDto> : IMappingService<TEntity, TDto>
    where TEntity : class 
    where TDto : class
{
    public abstract TDto EntityToDto(TEntity entity);

    public abstract TEntity DtoToEntity(TDto dto);

    public virtual IEnumerable<TDto> EntitiesToDtos(IList<TEntity> entities)
    {
        return entities.Select(EntityToDto);
    }

    public virtual IEnumerable<TEntity> DtosToEntities(IList<TDto> dtos)
    {
        return dtos.Select(DtoToEntity);
    }
}

以下是两个为两个实体实现这些实例的映射器作为示例:

public class ParentEntityMapper : MapperBase<ParentEntity, ParentEntityDto> , IParentEntityMapper
{

    private readonly IChildEntityMapper _childEntityMapper;

    public ParentEntityMapper(IChildEntityMapper  childEntityMapper)
    {
        _childEntityMapper = childEntityMapper;
    }

    public override ParentEntityDto EntityToDto(ParentEntity entity)
    {
        var dto = new ParentEntityDto();
        dto.Id = entity.Id;
        dto.Title = entity.Title;
        dto.Description = entity.Description;

        if (entity.ChildEntities != null)
        {
            dto.ChildEntities= _childEntityMapper.EntitiesToDtos(entity.ChildEntities .ToList()).ToList();
        }

        return dto;
    }

    public override ParentEntity DtoToEntity(ParentEntityDto dto)
    {
        // This is just a reverse of the above function
    }
}

子实体映射器:

    public class ChildEntityMapper : MapperBase<ChildEntity, ChildEntityDto>, IChildEntityMapper
    {
        private readonly ParentEntityMapper _parentEntityMapper;

        public ChildEntityMapper(IParentEntityMapper parentEntityMapper)
        {
            _parentEntityMapper = parentEntityMapper;
        }
        public override ChildEntityDto EntityToDto(ChildEntity entity)
        {
            var dto = new ChildEntityDto();

            dto.Id = entity.Id;
            dto.Description = entity.Description;

            if (entity.ParentEntity != null)
            {
                dto.ParentEntity = _parentEntityMapper.EntityToDto(entity.Image);
            }

            return dto;
        }

        public override Anchor DtoToEntity(AnchorDto dto)
        {
            // Just a reverse of the above function
        }
    }

所以我相信你们都能看到循环依赖发生的地方。在过去的两天里,我一直在为此感到痛苦,但我仍然非常业余,到目前为止还无法用Google解决这个问题。

我不想在这部分使用AutoMapper和ValueInjector之类的工具,因为我在数据库和DTO之间进行映射,我已经用它来在UI层中的DTO和ViewModel之间进行映射。

如果可能的话,我希望能够以这种方式保持两者之间的映射,因为有时候我只会请求子实体,在这种情况下,我可能想要获得父实体。

有谁知道如何才能做到这一点?

2 个答案:

答案 0 :(得分:1)

我想我看到你的“循环依赖”在这里发挥作用,但只是为了明确,这就是我认为会发生的事情。您在ParentEntity上调用EntityToDto,而ParentEntity又调用所有ChildEntity元素上的EntityToDto。这些ChildEntity元素中的每一个都会在ParentEntity上调用EntityToDto,然后我们又回到了我们开始的地方。

如果是这种情况,我的建议很简单:不要让父实体关注父实体托管EntityToDto。我会解决树的根,然后让下面的所有内容继续向下解析,直到树完成。

如果您这样做,它将阻止子实体将父级分配给自己。如果您需要能够从子项遍历到父项,那么在EntityToDto调用完成后,我将让父项将子项作为父项分配给子项。

答案 1 :(得分:0)

在进一步思考之后,我决定不再使它变得复杂,以至于我不允许通过映射器使用它的父节点来获取子实体,而是在我的服务层中为少数特定场景处理它。

任何有相同问题的人也可以查看我在reddit上创建的this主题以获取更多信息。