所以我最近发现,您可以通过为Func<T, TResult>
扩展方法而不是表达式指定.Select()
来强制Entity Framework不将您的投影转换为SQL。当您想要转换查询数据时,这很有用,但转换应该在您的代码而不是数据库中进行。
例如,当使用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数据库可以执行的操作,但是根据this article Func谓词不是&#39;翻译。
一旦我开始工作,创建以下扩展方法并没有太大的飞跃:
public static IQueryable<T> Materialize<T>(this IQueryable<T> q)
{
return q.Select(new Func<T, T>(t => t)).AsQueryable();
}
所以我的问题是 - 在使用它时我是否应该警惕任何陷阱?是否存在性能影响 - 要么将此无操作投影注入查询管道,要么导致EF不将.Where()
子句发送到服务器,从而通过线路发送所有结果? < / p>
目的是仍然使用.Where()
方法过滤服务器上的结果,然后在.Materialize()
之前使用.Select()
,以便提供商不会尝试将投影转换为SQL Server:
return Context.Record
.Where(r => // Some filter to limit results)
.Materialize()
.Select(r => // Some projection to DTO, etc.);
答案 0 :(得分: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.);
您的Materialise方法也没有理由返回IQueryable
,因为它不再是真正的IQueryable
转换为其他查询。它只是IEnumerable
。
在性能方面你应该没问题。实现之前的所有内容都在数据库中进行评估,并在代码中实现后实现。此外,在您和我的示例中,查询仍然延迟执行 - 在枚举查询之前不会执行查询。
答案 1 :(得分:0)
但是有一个问题:获取到客户端的列数。在第一种情况下,它会像select Status from Record
和另一个select Status, field2, field3, field4 from Record