AutoMapper.ProjectTo 正在本地评估带有条件的查询

AutoMapper.ProjectTo with criteria query is being evaluated locally

我正在使用 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 的最终投影 MySimpleEntity 而不是某些 Automapper 的内部 DTO 实体?

更新:

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

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

更新 3:

正如一些人所指出的,原因是 Linq-to-entities 本身而不是 Automapper。因此,为新的 SO 问题创建了一个新的、不太复杂的可重现案例,并在此处解决:

问题与AutoMapper无关,而是基于接口成员的谓词表达式。 Convert 是 C# cast 的等效表达式,如您所见,它将实体类型强制转换为 接口 包含 属性 (dtoMyOriginalEntity 是只是 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 约束更安全。