Automapper ProjectTo 将 ToList 添加到子属性中

2023-12-08

我使用 Entity Framework Core 使用投影将实体类映射到 DTO。但是,投影将 ToList 添加到子集合属性中,这会大大减慢查询速度。

公司实体:

public class Company
{
    public Company()
    {
        Employees = new List<CompanyEmployee>();
    }

    public string Address { get; set; }
    public virtual ICollection<CompanyEmployee> Employees { get; set; }
    ...
}

公司DTO:

public class CompanyDTO
{
    public CompanyDTO()
    {
        CompanyEmployees = new List<EmployeeDTO>();
    }

    public string Address { get; set; }
    public List<EmployeeDTO> CompanyEmployees { get; set; }
    ...
}

配置:

CreateMap<Company, CompanyDTO>()
    .ForMember(c => c.CompanyEmployees, a => a.MapFrom(src => src.Employees));
CreateMap<CompanyEmployee, EmployeeDTO>();

Query:

UnitOfWork.Repository<Company>()
    .ProjectTo<CompanyDTO>(AutoMapper.Mapper.Configuration)
    .Take(10)
    .ToList();

使用以下命令检查生成的查询Expression之后的财产ProjectTo产生以下结果:

Company.AsNoTracking()
    .Select(dtoCompany => new CompanyDTO() 
    {
        Address = dtoCompany.Address, 
        ...
        CompanyEmployees = dtoCompany.Employees.Select(dtoCompanyEmployee => new EmployeeDTO() 
                            {
                                CreatedDate = dtoCompanyEmployee.CreatedDate, 
                                ...
                            }).ToList() // WHY??????
    })

That ToList调用会导致对每个实体运行选择查询,这不是我想要的,正如您所猜测的那样。我测试了没有这个查询ToList(通过手动复制表达式并运行它)并且一切都按预期进行。如何防止 AutoMapper 添加该调用?我尝试改变List输入 DTO 到IEnumerable但什么都没有改变..


让我们忽略 EF Core 的影响ToList调用并专注于 AutoMapperProjectTo.

该行为被硬编码在EnumerableExpressionBinder class:

expression = Expression.Call(typeof(Enumerable), propertyMap.DestinationPropertyType.IsArray ? "ToArray" : "ToList", new[] { destinationListType }, expression);

此类属于 AutoMapper QueryableExtensions 处理管道的一部分,负责将源可枚举类型转换为目标可枚举类型。正如我们所看到的,它总是发出ToArray or ToList.

事实上,当目标成员类型是ICollection<T> or IList<T>, the ToList需要调用,否则表达式将无法编译。但是当目标成员类型是IEnumerable<T>,这是任意的。

因此,如果您想摆脱上述场景中的这种行为,您可以注入一个自定义IExpressionBinder before the EnumerableExpressionBinder(活页夹按顺序调用,直到IsMatch回报true) 像这样 (

namespace AutoMapper
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using AutoMapper.Configuration.Internal;
    using AutoMapper.Mappers.Internal;
    using AutoMapper.QueryableExtensions;
    using AutoMapper.QueryableExtensions.Impl;

    public class GenericEnumerableExpressionBinder : IExpressionBinder
    {
        public bool IsMatch(PropertyMap propertyMap, TypeMap propertyTypeMap, ExpressionResolutionResult result) =>
            propertyMap.DestinationPropertyType.IsGenericType &&
            propertyMap.DestinationPropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>) &&
            PrimitiveHelper.IsEnumerableType(propertyMap.SourceType);

        public MemberAssignment Build(IConfigurationProvider configuration, PropertyMap propertyMap, TypeMap propertyTypeMap, ExpressionRequest request, ExpressionResolutionResult result, IDictionary<ExpressionRequest, int> typePairCount, LetPropertyMaps letPropertyMaps)
            => BindEnumerableExpression(configuration, propertyMap, request, result, typePairCount, letPropertyMaps);

        private static MemberAssignment BindEnumerableExpression(IConfigurationProvider configuration, PropertyMap propertyMap, ExpressionRequest request, ExpressionResolutionResult result, IDictionary<ExpressionRequest, int> typePairCount, LetPropertyMaps letPropertyMaps)
        {
            var expression = result.ResolutionExpression;

            if (propertyMap.DestinationPropertyType != expression.Type)
            {
                var destinationListType = ElementTypeHelper.GetElementType(propertyMap.DestinationPropertyType);
                var sourceListType = ElementTypeHelper.GetElementType(propertyMap.SourceType);
                var listTypePair = new ExpressionRequest(sourceListType, destinationListType, request.MembersToExpand, request);
                var transformedExpressions = configuration.ExpressionBuilder.CreateMapExpression(listTypePair, typePairCount, letPropertyMaps.New());
                if (transformedExpressions == null) return null;
                expression = transformedExpressions.Aggregate(expression, (source, lambda) => Select(source, lambda));
            }

            return Expression.Bind(propertyMap.DestinationProperty, expression);
        }

        private static Expression Select(Expression source, LambdaExpression lambda)
        {
            return Expression.Call(typeof(Enumerable), "Select", new[] { lambda.Parameters[0].Type, lambda.ReturnType }, source, lambda);
        }

        public static void InsertTo(List<IExpressionBinder> binders) =>
            binders.Insert(binders.FindIndex(b => b is EnumerableExpressionBinder), new GenericEnumerableExpressionBinder());
    }
}

它基本上是一个修改后的副本EnumerableExpressionBinder与不同的IsMatch检查并删除ToList调用发出代码。

现在,如果您将其注入到 AutoMapper 配置中:

Mapper.Initialize(cfg =>
{
    GenericEnumerableExpressionBinder.InsertTo(cfg.Advanced.QueryableBinders);
    // ...
});

并设置您的 DTO 集合类型IEnumerable<T>:

public IEnumerable<EmployeeDTO> CompanyEmployees { get; set; }

the ProjectTo将生成表达式Select但没有ToList.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Automapper ProjectTo 将 ToList 添加到子属性中 的相关文章

  • 在模板类中声明模板友元类时出现编译器错误

    我一直在尝试实现我自己的链表类以用于教学目的 我在迭代器声明中指定了 List 类作为友元 但它似乎无法编译 这些是我使用过的 3 个类的接口 Node h define null Node
  • 类型中的属性名称必须是唯一的

    我正在使用 Entity Framework 5 并且有以下实体 public class User public Int32 Id get set public String Username get set public virtual
  • 机器Epsilon精度差异

    我正在尝试计算 C 中双精度数和浮点数的机器 epsilon 值 作为学校作业的一部分 我在 Windows 7 64 位中使用 Cygwin 代码如下 include
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • 传递给函数时多维数组的指针类型是什么? [复制]

    这个问题在这里已经有答案了 我在大学课堂上学习了 C 语言和指针 除了多维数组和指针之间的相似性之外 我认为我已经很好地掌握了这个概念 我认为由于所有数组 甚至多维 都存储在连续内存中 因此您可以安全地将其转换为int 假设给定的数组是in
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • 重载 (c)begin/(c)end

    我试图超载 c begin c end类的函数 以便能够调用 C 11 基于范围的 for 循环 它在大多数情况下都有效 但我无法理解和解决其中一个问题 for auto const point fProjectData gt getPoi
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • 为什么 isnormal() 说一个值是正常的,而实际上不是?

    include
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • C# 中的 IPC 机制 - 用法和最佳实践

    不久前我在 Win32 代码中使用了 IPC 临界区 事件和信号量 NET环境下场景如何 是否有任何教程解释所有可用选项以及何时使用以及为什么 微软最近在IPC方面的东西是Windows 通信基础 http en wikipedia org
  • 使用特定参数从 SQL 数据库填充组合框

    我在使用参数从 sql server 获取特定值时遇到问题 任何人都可以解释一下为什么它在 winfom 上工作但在 wpf 上不起作用以及我如何修复它 我的代码 private void UpdateItems COMBOBOX1 Ite
  • 对于某些 PDF 文件,LoadIFilter() 返回 -2147467259

    我正在尝试使用 Adob e IFilter 搜索 PDF 文件 我的代码是用 C 编写的 我使用 p invoke 来获取 IFilter 的实例 DllImport query dll SetLastError true CharSet
  • C# 中最小化字符串长度

    我想减少字符串的长度 喜欢 这串 string foo Lorem ipsum dolor sit amet consectetur adipiscing elit Aenean in vehicula nulla Phasellus li
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l
  • 如何确定 CultureInfo 实例是否支持拉丁字符

    是否可以确定是否CultureInfo http msdn microsoft com en us library system globalization cultureinfo aspx我正在使用的实例是否基于拉丁字符集 我相信你可以使

随机推荐

  • 如何为 request.getRequestDispatcher() 指定 .JSP 文件的路径?

    我对 request getContextPath 的含义感到困惑 我的文件布局如下 MyServer WebContent Resources MyImage jpg Resources Scripts MyScript js WEB I
  • 如何从类型脚本读取缓冲区数据?(Solana)

    如何从类型脚本读取缓冲区数据 我想使用公钥来获取我拥有的所有令牌列表 我试图得到这个 但返回的是一个空的对象数组 import Connection Keypair from solana web3 js const Solana new
  • 读取 C 中的分数

    如何将分数读入 C 中以进行数学运算 分数将包含斜杠符号 例如 用户将输入3 12 一个字符串 程序将找到 gcd 计算约简分数并得出 1 4 我最初的计划是使用 strtok 函数本身获取分子和分母 但我遇到了将分子和分母存储到单独变量中
  • Ruby 私有 attr_accessor 和意外的 nil

    当我经常使用 Ruby 时 我有一个坏习惯 将所有内容公开而忽略隐私 不幸的是 这种无知又回来困扰着我 这是我的问题的一个简单版本 class Something private attr accessor sneaky public de
  • SharePoint - 发生意外错误

    在 SharePoint 中 当我转到新的 Web 部件页面时 我收到以下信息 Error 发生意外的错误 Web 部件维护页面 如果您有权限 可以使用此页面暂时关闭 Web 部件或删除个人设置 有关详细信息 请联系您的站点管理员 解决 W
  • 是否有一个 Java XML API 可以解析文档而不解析字符实体?

    我有一个程序需要解析包含字符实体的 XML 程序本身不需要解决它们 并且它们的列表很大并且会发生变化 所以如果可以的话 我想避免对这些实体的显式支持 这是一个简单的例子
  • 在 INSERT 语句中使用 ROWLOCK (SQL Server)

    在复制大量数据并将其插入同一个表的插入语句上使用 ROWLOCK 是否明智 Ex INSERT INTO TABLE with rowlock id name SELECT newid name FROM TABLE with nolock
  • C++ 排序和跟踪索引

    使用 C 并希望使用标准库 我想按升序对样本序列进行排序 但我也想记住新样本的原始索引 例如 我有一组样本 向量或矩阵A 5 2 1 4 3 我想将这些排序为B 1 2 3 4 5 但我还想记住这些值的原始索引 这样我就可以获得另一组 C
  • Windows 版本的 wcswidth_l

    我有一些文本要写入 Windows 控制台 我需要知道列的实际宽度 wcswidth l 似乎是拥有它的平台上的最佳选择 尽管mbswidth l 会更好 因为我不想使用 wchar t 但由于某种原因它不存在 但除了其他平台之外 我还需要
  • Reflection Help - 根据另一个对象设置对象的属性

    我需要一些反思的帮助 我将一个对象传递给另一个对象的构造函数 我需要循环遍历参数的属性并基于它设置新对象属性 大多数 但不是全部 params 属性都存在于新对象中 到目前为止 我已经有了基本的框架 public DisabilityPay
  • Git远程服务器Push失败

    从远程服务器克隆 Git 存储库后 我在本地副本上提交了一些更改 我想将这些更改推送回远程服务器 但收到一条错误消息 该消息没有产生任何有用的信息 fatal read error Invalid argument Ps 服务器和本地rep
  • PDO - 获取 COUNT(*) 的结果?

    在新用户注册过程中 我试图查找用户名或用户电子邮件是否已在数据库中 为此 我想查找标识符 电子邮件或用户名 与数据库中的记录匹配的行数 如果我没有搞砸 唯一可能的返回值是 0 或 1 我的函数如下 但我需要帮助才能完成它 function
  • 客户端验证无法使用 validate() 进行工作

    我正在 validate 方法中进行验证 public void validate if continent equals 1 HttpServletRequest request ServletActionContext getReque
  • 以编程方式删除按钮上的事件侦听器

    我有一个注册的按钮onclick事件如图所示
  • MySQL自动递增每个在其他列中输入的不同值? [关闭]

    Closed 这个问题需要细节或清晰度 目前不接受答案 我想在我的 mysql 数据库中创建一个列 该列会自动递增以响应输入到另一列的特定值 例如 下面的数据 当学生使用该网站创建新记录时 他 她将输入班级名称并分配一个数字变量 特定于该班
  • 创建一个返回表的 Oracle 函数

    我正在尝试在包中创建一个返回表的函数 我希望在包中调用该函数一次 但能够多次重复使用其数据 虽然我知道我在 Oracle 中创建了临时表 但我希望保持干燥 到目前为止 这就是我所拥有的 Header CREATE OR REPLACE PA
  • 如何使用 dplyr 或 base R 计算链函数中 T/F 观测值的数量?

    假设我有一个名为 tbl dfpokemons像这样 X Name Type 1 Type 2 Total HP Attack Defense Sp Atk Sp Def Speed Generation Legendary int fct
  • copy 和 mutableCopy 如何应用于 NSArray 和 NSMutableArray?

    有什么区别copy and mutableCopy当用于任一NSArray or an NSMutableArray 这是我的理解 这是对的吗 NSArray NSArray myArray imu NSArray arrayWithObj
  • 如何将元素 id 放入 PHP 变量中

    是否可以获取元素id into a PHP多变的 假设我有许多带有 ID 的元素 span class myElement span span class myElement span 我怎样才能把它变成PHP变量以便提交查询 我想我必须重
  • Automapper ProjectTo 将 ToList 添加到子属性中

    我使用 Entity Framework Core 使用投影将实体类映射到 DTO 但是 投影将 ToList 添加到子集合属性中 这会大大减慢查询速度 公司实体 public class Company public Company Em