这种扩展方法是否有效地实现了我的IQueryable?

时间:2012-09-12 11:31:26

标签: linq entity-framework-5 deferred-execution

所以我最近发现,您可以通过为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.);

2 个答案:

答案 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