【问题标题】:AutoMapper.ProjectTo with criteria query is being evaluated locally正在本地评估带有条件查询的 AutoMapper.ProjectTo
【发布时间】:2019-12-14 06:08:28
【问题描述】:

我正在使用 Automapper 可查询扩展:http://docs.automapper.org/en/stable/Queryable-Extensions.html#

我想将我的完整实体映射到仅包含几个字段的简化版本。在这种情况下,我的前端 UI 将不知道基本实体,只会发送简化实体的过滤条件。因此,我使用query.ProjectTo<MySimpleEntity>,然后将Where 与过滤条件一起应用。

在这种特定情况下,两个实体都实现了我的自定义接口ITimeLimitedEntity,它确保了ValidFromValidTo 字段。

当我不使用 Automapper 并对整个实体应用条件时,过滤会按预期工作并生成正确的 SQL 查询。

但只要我添加 ProjectTo<MySimpleEntity>,我就会收到一个警告(我实际上将其作为异常抛出以避免无声的客户端评估):

The LINQ expression 'where ((Convert(new MySimpleEntity() {Id = [dtoMyOriginalEntity].Id, Name = [dtoMyOriginalEntity].Name, ValidFrom = [dtoMyOriginalEntity].ValidFrom}, ITimeLimitedEntity).ValidFrom == null))'
 could not be translated and will be evaluated locally.'

因此,该标准应用于 Automapper 的内部 dtoMyOriginalEntity,当然,它并没有实现我的 ITimeLimitedEntity,因此 LINQ 添加了 Convert,这显然不能转换为有效的 SQL 查询,从而导致尝试在内存中对其进行评估。

这很奇怪。当我调试代码时:

 projQuery = query.ProjectTo<MySimpleEntity>(_mapper.ConfigurationProvider);

projQuery 是 IQueryable,它实现了ITimeLimitedEntity——那为什么 Linq 需要转换呢?

我如何告诉 Automapper 或 Linq 它应该将我的 where 标准应用到已经是 ITimeLimitedEntity 而不是某些 Automapper 的内部 DTO 实体的最终投影 MySimpleEntity 上?

更新:

我创建了一个简单的 dotnetfiddle 示例 https://dotnetfiddle.net/X2RDGn 当然,它工作得很好,因为它使用的是内存数据库。但我已经评论了在现实世界中失败的问题领域。

更新 2: 我使用真实数据库创建了一个简化的控制台应用 gist.github.com/progmars/eeec32a533dbd2e1f85e551db1bc53f8,当通过通用方法访问时,Linq 表达式仍在本地执行,尽管调试器显示两个表达式主体完全匹配。

更新 3:

正如一些人指出的那样,原因是 Linq-to-entities 本身而不是 Automapper。因此,为一个新的 SO 问题创建了一个新的、不那么复杂的可重现案例,并在这里解决了:Why Linq "where" expression after Select gets evaluated locally when created through a generic method?

【问题讨论】:

  • 写一个 LINQ 查询来做你想做的事,没有 AM,然后我们看看ProjectTo 能做什么。
  • @LucianBargaoanu 我用 dotnetfiddle 示例的链接更新了我的问题。
  • 因此,如果 LINQ 不能与 EF 一起使用,那么它也不能与 AM 一起使用。

标签: c# linq entity-framework-core automapper


【解决方案1】:

问题与AutoMapper无关,而是基于接口成员的谓词表达式。 Convert 是 C# cast 的表达式等价物,如您所见,它将实体类型转换为包含属性的 interfacedtoMyOriginalEntity 只是 lambda 表达式 参数的名称)。

这是接口约束泛型方法的已知行为。解决方案是添加class 约束。将其应用于您的示例:

public class CriteriaSpecificationForSomeable<T> : ISpecification<T> where T: class, ISomeable
// ----------------------------------------------------------------------------^^^
{
    private Expression<Func<T, bool>> _someCriteria;

    public CriteriaSpecificationForSomeable()
    {
        _someCriteria = e => (e.Some == "Hello");
    }

    public virtual Expression<Func<T, bool>> Criteria { get { return _someCriteria; } }
}

演员 (Convert) 不见了。

顺便说一句,这已在最新的 EF Core 2.2.6 中修复,并且原始代码在那里工作(可能您使用的是较旧的 EF Core 版本)。但是 EF6 也有同样的问题,所以通常在针对接口约束泛型类型创建表达式时添加 class 约束会更安全。

【讨论】:

  • 谢谢,这一定是朝着正确方向迈出的一步,但仍然没有工作到最后。我使用真实数据库创建了一个简化的控制台应用程序gist.github.com/progmars/eeec32a533dbd2e1f85e551db1bc53f8,当通过通用接口访问时,Linq 表达式仍在本地执行,尽管调试器显示两个表达式主体完全匹配。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-04
  • 2013-03-09
  • 1970-01-01
  • 2020-12-27
相关资源
最近更新 更多