2

所以我最近发现你可以通过指定扩展方法而不是表达式Func<T, TResult>来强制实体框架不要将你的投影转换为 SQL。.Select()当您想要转换查询的数据时,这很有用,但这种转换应该发生在您的代码中而不是数据库中。

例如,当使用 EF5 的新 Enum 支持并尝试将其投影到 DTO 中的字符串属性时,这会失败:

results.Select(r => new Dto { Status = r.Status.ToString() })

这会起作用:

results.Select(new Func<Record, Dto>(r => new Dto { Status = r.Status.ToString() }));

因为在第一种(表达式)情况下,EF 无法弄清楚如何将 Status.ToString() 转换为 SQL 数据库可以执行的操作,但是根据这篇文章Func 谓词不会被翻译。

一旦我完成了这项工作,创建以下扩展方法并没有太大的飞跃:

public static IQueryable<T> Materialize<T>(this IQueryable<T> q)
{
    return q.Select(new Func<T, T>(t => t)).AsQueryable();
}

所以我的问题是 - 使用它时有什么我应该警惕的陷阱吗?是否存在性能影响 - 要么将此无操作投影注入查询管道,要么导致 EF 不将.Where()子句发送到服务器,从而通过线路发送所有结果?

目的是仍然使用一种.Where()方法来过滤服务器上的结果,然后使用.Materialize()before.Select()以便提供程序不会尝试将投影转换为 SQL Server:

return Context.Record
    .Where(r => // Some filter to limit results)
    .Materialize()
    .Select(r => // Some projection to DTO, etc.);
4

2 回答 2

2

简单地使用AsEnumerable应该做同样的事情:

return Context.Record
              .Where(r => // Some filter to limit results)
              .AsEnumerable() // All extension methods now accept Func instead of Expression
              .Select(r => // Some projection to DTO, etc.);

您的 Materialize 方法也没有理由返回,IQueryable因为它不再是真正IQueryable翻译成另一个查询。它只是IEnumerable

就性能而言,你应该没问题。实现之前的所有内容都在数据库中进行评估,实现之后的所有内容都在您的代码中进行评估。此外,在您和我的示例查询中,查询仍然延迟执行 - 直到枚举查询时才会执行。

于 2012-09-12T12:11:05.083 回答
0

但是有一个问题:获取到客户端的列数。在第一种情况下,它会像select Status from Record在另一种情况下select Status, field2, field3, field4 from Record

于 2013-10-04T14:22:40.497 回答