展平 LINQ/AutoMapper ProjectTo 嵌套 children

Flatten LINQ/AutoMapper ProjectTo nested children

我已经查看了很多与此问题相关的 SO 文章,并使用了其中的一些 questions/answers 来接近我的情况的答案,但我不能完全正确。

我有一个包含 5 tables 的 EF6 上下文(Grandparent、GrandparentParent、Parent、ParentChild,和 Child)。 GP 和 PC table 是简单的 many-to-many 关系,将家族等级联系在一起。

我的业务需求是查询数据库和 return grandparent objects 的列表,其中包含他们所有 grandchildren 的列表,但没有 grand children 嵌套在 parent 下。我有 3 个 ViewModel classes,它们是它们对应的 tables 的子集,我想将数据库层次结构扁平化为 GrandparentViewModel classes 的列表,其中 GVM =

public class GrandparentViewModel
{
    public int GrandparentId { get; set; }
    public string Name { get; set; }

    public List<ChildViewModel> Grandchildren { get; set; }
}

同样重要的是要注意,我在查询中使用了 AutoMapper 的 ProjectTo<> 扩展方法,因此我可以卸载投影并且仅 select 每个 table 的字段子集。 ..即...我在每个 table 上都有 DateOfBirth,我不想查询 and/or return 那个字段。

在我试图让它工作的过程中,我偶然发现了 AutoMapper 的 ITypeConverter 接口并构建了一个 class 实现了 grandparent 和 child 的接口。这是那些 classes:

public class GrandparentConverter : ITypeConverter<Grandparent, GrandparentViewModel>
{
    public GrandparentViewModel Convert(ResolutionContext context)
    {
        var entities = context.SourceValue as Grandparent;

        return entities
            .Select(x => new GrandparentViewModel
            {
                GrandparentId = x.GrandparentId,
                Name = x.Name,
                Grandchildren = AutoMapper.Mapper.Map<IEnumerable<Parent>, List<ChildViewModel>>(x.Parents)
            }).ToList();

        //return new GrandparentViewModel
        //{
        //    GrandparentId = entities.GrandparentId,
        //    Name = entities.Name,
        //    Grandchildren = entities.Parents.SelectMany(x => x.Children.Select(y => new ChildViewModel
        //    {
        //        ChildId = y.ChildId,
        //        Name = y.Name
        //    }).ToList()).ToList()
        //};
    }
}


public class ChildConverter : ITypeConverter<IEnumerable<Parent>, List<ChildViewModel>>
{
    public List<ChildViewModel> Convert(ResolutionContext context)
    {
        var entities = context.SourceValue as IEnumerable<Parent>;

        return entities
            .SelectMany(x => x.Children)
            .Select(x => new ChildViewModel
            {
                ChildId = x.ChildId,
                Name = x.Name
            }).ToList();
    }
}

消费代码如下:

MapperConfiguration mapper = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<Child, ChildViewModel>();
            cfg.CreateMap<IEnumerable<Parent>, List<ChildViewModel>>().ConvertUsing<ChildConverter>();

            cfg.CreateMap<Grandparent, GrandparentViewModel>();
            cfg.CreateMap<IEnumerable<Grandparent>, List<GrandparentViewModel>>().ConvertUsing<GrandparentConverter>();
        });

        FamilyEntities db = new FamilyEntities();
        List<GrandparentViewModel> entities = db.Set<Grandparent>()
            .Where(x => x.GrandparentId == 1)
            .ProjectTo<GrandparentViewModel>(mapper)
            .ToList();

目前,该解决方案构建、运行并 return 一个单一的 GrandparentViewModel class 正如我所期望的,但是所有字段(包括 grandparent...即...名称)为空。

关于我遗漏的任何想法and/or为什么没有填充数据?我尝试在该 TypeConverter class 中设置一个断点,但即使在 AutoMapper 的 MapperConfiguration class 中指定的唯一映射配置正在使用该转换器,它也永远不会执行。如果我删除那个 CreateMap 调用,AutoMapper 就会抛出它的 "Missing configuration" 错误。

非常感谢任何建议。

编辑:我构建了这个问题的缩小版本,当我不构建 AutoMapper MapperConfiguration 并将其传递给 ProjectTo 方法时,自定义 TypeConverter class 中的断点被命中。因此,在我原来的 post 中,问题似乎是 AutoMapper 实际上并未使用 ConvertUsing 方法中指定的转换器。

已确认这是 AutoMapper 的 ProjectTo 逻辑中的错误。 Issue 1216 已使用 AutoMapper 记录以更正此问题。与此同时,我得到了一个似乎有效的解决方法。

cfg.CreateMap<Grandparent, GrandparentViewModel>()
   .ForMember(d => d.Grandchildren, 
       opt.MapFrom(s => s.Parents.SelectMany(x => x.Children));