如果您的 OData 模型直接映射到 EF 模型并且传递了一个IQueryable<T>
表达式,OK()
则查询将作为 SQL 显式传递给数据库引擎:
SELECT * FROM Cidades WHERE nome LIKE '%Florianopolis%'
发生这种情况时,数据库连接中的排序规则设置将确定比较匹配逻辑。
当您的数据库排序规则不区分大小写和重音时,但您的数据仍然被过滤,就好像它不是一样,那么这表明 anIEnumerable<T>
已被传入OK()
并且正在 C# 中评估比较逻辑,默认情况下对这两种情况都不敏感和口音。不幸的是,这意味着很可能整个数据表都已首先加载到内存中,以便可以应用过滤器。
在您的情况下,OData 模型映射到 DTO 表达式,这些表达式通过AutoMapper映射到 EF 模型,这就是生成的查询可以分解的地方。通过调用Map()
,您将从 EF 表中加载所有$filter
记录,并将条件留给EnableQueryAttribute
要自动应用 OData 查询约定,您必须从您的方法返回一个IQueryable<T>
,或者至少将一个传递IQueryable<T>
给OK()
响应处理程序。使用 AutoMapper,您可以使用 Queryable Extensions 来满足IQueryable<T>
要求:
可查询扩展
当使用诸如 NHibernate 或实体框架之类的 ORM 和 AutoMapper 的标准 mapper.Map 函数时,您可能会注意到,当 AutoMapper 尝试将结果映射到目标类型时,ORM 将查询图中所有对象的所有字段.
...
ProjectTo
必须是链中的最后一个调用。ORM 与实体一起工作,而不是 DTO。因此,对实体应用任何过滤和排序,作为最后一步,投影到 DTO。
在 OData 中,最后一个要求 (about ProjectTo
) 仍然存在问题,因为它EnableQueryAttribute
会将查询选项附加到IQueryable<T>
响应中,最终仍然会先将整个表具体化到内存中 ( IEnumerable<T>
),然后再应用过滤器,这仍然非常低效。当有人抱怨 OData 实现的性能不佳时,通常会观察到这种行为,它并不总是 AutoMapper,而是通常将数据源全部加载到内存中然后过滤的模式。遵循 AutoMapper 的默认指南将引导您朝这个方向发展。
相反,我们需要使用一个额外的包:AutoMapper.Extensions.ExpressionMapping,它可以让我们访问UseAsDataSource
扩展方法。
UseAsDataSource 将
表达式相互映射是一件乏味的事情,并且会产生长而丑陋的代码。
UseAsDataSource().For<DTO>()
不必显式映射表达式,从而使这种翻译变得干净。ProjectTo<TDO>()
在适用的情况下,它也需要您。
这会将您的实现更改为以下内容:
[HttpGet]
[EnableQuery]
public ActionResult<IQueryable<CidadeDTO>> Get()
{
return Ok(_db.Cidades.UseAsDataSource().For<CidadeDTO>());
}
不要陷入假设 AutoMapper 是 OData API 实现的必要或最佳实践的陷阱。如果您没有使用 AutoMapper 提供的独特功能,那么添加额外的抽象层最终可能会使您的解决方案过于复杂。
我不反对 AutoMapper,我经常将它用于集成、ETL、GraphQL 和非 DDD 样式的数据模式,其中 DTO 模型与底层 EF/数据存储模型有很大不同。但是,简单的 DDD 数据模型和基于 OData API 的解决方案可以轻松避免维护和性能开销。
AutoMapper 是一种基于约定的 ORM,当您想要更改代码中实现层之间的结构时,它可能很有用,传统上您可以将可能表示聚合或具有扁平结构的业务领域模型映射到高度规范化的数据库模型。
OData也是一个基于约定的 ORM。它旨在促进 AutoAmpper 提供的许多相同操作,但 Flattening 和 Unflattening 模型除外。这些操作被推迟到 EF 引擎。通过 OData 映射公开的类型是DTO
如果您的 DTO 模型与您的 EF 模型具有相同的关系结构,那么您通常根本不会使用 AutoMapper,OData Edm 映射专门针对管理此类工作负载进行了优化,并且旨在并且已经直接集成到序列化中层,使 Edm 真正成为仅存在于网络和客户端中的数据传输对象。