更新:
自动映射器already adds a ToList()在简单的情况下会自动应用它。我看到的导致我打开这个问题的问题原来是一个更复杂的问题(SoftwareIds memberN+1的元凶。请参阅this。)。



在EF Core 2.1中,我们获得了在LINQ子查询上添加ToList()以缓冲结果并避免N + 1数据库查询的支持。 (Docs)在针对DbContext的普通LINQ查询上非常有用。

但是,如果我有一个Automapper配置文件,它会导致N + 1个查询:

    public MyMappingProfile() =>
        CreateMap<MyEntity, MyDto>().ForMember(e => e.MyCollectionProp, o => o.MapFrom(l => l.MyCollectionPropMany.Select(la => la.MyCollectionEntity)))


添加ToList()会引发异常:

    public MyMappingProfile() =>
        CreateMap<MyEntity, MyDto>().ForMember(e => e.MyCollectionProp, o => o.MapFrom(l => l.MyCollectionPropMany.Select(la => la.MyCollectionEntity).ToList()))



  System.NotSupportedException:'无法解析表达式
  'MyDto.MyCollectionPropMany.Select(la =>
  la.MyCollectionEntity).ToList()':方法的重载
  目前不支持“ System.Linq.Enumerable.ToList”。


有没有一种方法可以在Automapper配置文件中启用子查询缓冲?

楷模:

public class MyEntity
{
    public int Id { get; set; }
    public ICollection<MyCollectionPropMany> MyCollectionPropManys { get; set; }
    ...
}

public class MyCollectionPropMany
{
    public int MyEntityId { get; set; }
    public MyEntity MyEntity { get; set; }
    public int MyCollectionPropId { get; set; }
    public MyCollectionProp MyCollectionProp { get; set; }
}

public class MyCollectionProp
{
    public int Id { get; set; }
    public ICollection<MyCollectionPropMany> MyCollectionPropManys { get; set; }
    ...
}

public class MyDto
{
    public int Id { get; set; }
    public IEnumerable<MyCollectionPropDto> MyCollectionPropDtos { get; set; }
    ...
}

public class MyCollectionPropDto
{
    public string Name { get; set; }
    ...
}


Automapper v7.0.1

真实的场景(我尝试简化SO的类型/使之通用):Source在这个真实的示例中,通过多对多的LanguagesTags成员当前正在生成N + 1查询。

最佳答案

事实证明,当映射可枚举类型时,AutoMapper有时会自动将ToList / ToArray添加到投影表达式中,有时却不会。

规则似乎如下。如果可以从源表达式类型直接分配目标可枚举类型,则AutoMapper将直接使用源表达式。换句话说,如果以下分配有效(伪代码):

dst.Member = src.Expression;


在这种情况下,由您决定是否在映射表达式中包含ToList(因此,选择加入EF Core相关查询优化)。

在所有其他情况下,如果需要,AutoMapper会执行可枚举的元素映射,然后添加ToArrayToList。无法选择退出。

简短地说,如果目标可枚举元素类型为Dto(需要映射),则在源LINQ表达式中不要包含ToList,如果它是基本类型或实体类型,则请包含ToList以避免N + 1查询。如果目标集合类型为IEnumerable<T>,则所有这些都适用。如果源表达式返回IReadOnlyCollection<T>,AutoMapper将自动处理其他任何派生的集合类型,例如IReadOnlyList<T>ICollection<T>IList<T>List<T>T[]IEnumerable<TSource>等。

关于c# - 使用Automapper优化相关子查询,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52368567/

10-13 03:33