EF 4.0 / FirstOrDefault方法的奇怪行为

时间:2010-08-22 06:07:57

标签: entity-framework-4

我正在使用EF 4.0和POCO开发一个小应用程序。

在测试我的应用程序时,我开始关注数据访问层的性能。所以我在尝试检索记录时解雇了SQL Profiler:

ctx.Orders.Include("OrderItems").FirstOrDefault<Order>(c => c.OrderID == id);

EF发出一条SQL语句,该语句将从服务器上的Orders表中检索所有记录,并因此返回DAL,此时L2E将选择满足条件的一个并返回它。

可以改变这种行为。

谢谢!

1 个答案:

答案 0 :(得分:9)

请试试这个:

ctx.Orders.Include("OrderItems").Where(c => c.OrderID == id).FirstOrDefault();


顺便说一下,您不需要查看SQL事件探查器以查看生成的SQL,您可以通过编写代码在代码内部执行以下操作:

IQueryable<Order> query = ctx.Orders.Include("OrderItems")
                                    .Where(c => c.OrderID == id);
string sql = ((ObjectQuery<Order>)query).ToTraceString();


修改
问:如果我们有像 FindOrders 这样的函数,我们需要将谓词传递给这个函数怎么办? 答:代码应如下所示:

public List<Order> FindOrders(Expression<Func<Order, bool>> predicate) { 
    using (DBContext ctx = new DBContext()) { 
        return ctx.Orders.Include("OrderItems").Where(predicate).ToList<Order>(); 
    } 
} 

//Calling the function:
var order = FindOrders(c => c.OrderID == id)[0];


这一次,如果您检查SQL事件探查器,您将看到SQL中有一个where子句已提交给SQL Server。

说明:
这种“怪异行为”的原因在于,当你编写Where(c =&gt; c.OrderID == id)时,C#编译器将你的lambda表达式转换为Expression&lt; Func&lt; TSource,int,bool&gt;&gt;而不是Func&lt; TSource,int,bool&gt;。

MSDN Documentation for Queryable.Where也证实了这一点:

public static IQueryable<TSource> Where<TSource>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, int, bool>> predicate
)

但是,如果您显式传递Func&lt; TSource,int,bool&gt;&gt;到Where方法那么你基本上是在调用Enumerable.Where:

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, int, bool> predicate
)

而且我们知道IEnumerable.Where是“LINQ to Objects”实现而不是“LINQ to Entities”,这意味着你可以访问你的IEnumerable.Where调用,ObjectQuery运行初始查询(ctx.Orders.Include(“OrderItems”))并将结果提供给IEnumerable.Where,以便它在客户端上为您过滤掉它。

另一方面,使用Queryable.Where调用(ctx.Orders.Include(“OrderItems”)。其中(c =&gt; c.OrderID == id).FirstOrDefault())在到达该点之前不会被执行我们称之为FirstOrDefault()函数,这意味着Queryable.Where然后被转换为本机SQL以及查询的其余部分,并将传递给SQL Server,因此您在SQL语句中看到Where子句肯定是期望的运行时行为。

顺便说一句,不要忘记将此命名空间导入您的类文件:
using System.Linq.Expressions;