所以我最近发现,您可以通过指定Func<T, TResult>
来强制 Entity Framework 不将您的投影转换为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 数据库可以执行的操作,但根据this article Func 谓词未翻译。
一旦我完成了这个工作,创建以下扩展方法就没有太大的飞跃:
public static IQueryable<T> Materialize<T>(this IQueryable<T> q)
{
return q.Select(new Func<T, T>(t => t)).AsQueryable();
}
所以我的问题是 - 使用它时有什么我应该警惕的陷阱吗?是否会对性能产生影响 - 要么将此不执行任何操作的投影注入(inject)查询管道,要么导致 EF 不发送 .Where()
子句发送到服务器,从而通过网络发送所有结果?
目的是仍然使用 .Where()
方法来过滤服务器上的结果,但随后使用 .Materialize()
之前.Select()
这样提供程序就不会尝试将投影转换为 SQL Server:
return Context.Record
.Where(r => // Some filter to limit results)
.Materialize()
.Select(r => // Some projection to DTO, etc.);
最佳答案
简单地使用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
。
就性能而言,你应该没问题。实现之前的所有内容都在数据库中进行评估,而实现之后的所有内容都在代码中进行评估。此外,在您和我的示例查询中,查询仍然延迟执行 - 在枚举查询之前它不会执行。
关于linq - 这个扩展方法是否有效地实现了我的 IQueryable?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12387180/