EF 4.0 / Weird behaviour of FirstOrDefault method

浪尽此生 提交于 2019-12-14 03:39:52

问题


I am developing a small application using EF 4.0 and POCO.

While testing my application, I grew concerned about the performance of the Data Access Layer. So I fired SQL Profiler to see that when trying to retrieve a record:

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

the EF issues a SQL statement that would retrieve all records from the Orders table on the Server and as such return to DAL at which time L2E would pick one thay meet the criteria and return it.

Can this behaviour be changed.

Thanks!

Zen


回答1:


Try this one please:

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


By the way you don't need to look into SQL Profiler to see the generated SQL, you can do it right inside your code by writing:

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


EDIT:
Question: What if we have a function like FindOrders and we need to pass the predicate to this function? Answer: The code should looks like:

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];


This time, if you check your SQL Profiler you'll see there is a where clause in the SQL that's been submitted to SQL Server.

Explanation:
The reason for this "Weird behavior" is that basically when you write Where(c => c.OrderID == id), C# compiler cast your lambda expression into an Expression<Func<TSource, int, bool>> and NOT to a Func<TSource, int, bool>.

MSDN Documentation for Queryable.Where also confirms this:

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

However if you explicitly pass a Func<TSource, int, bool>> to the Where method then you are basically calling Enumerable.Where:

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

And as we know IEnumerable.Where is "LINQ to Objects" implementation and NOT "LINQ to Entities" which means onces you reach to your IEnumerable.Where call, the ObjectQuery run the initial query (ctx.Orders.Include("OrderItems")) and gives the results to the IEnumerable.Where so that it will filter it out for you on the Client Side.

On the other hand the call with Queryable.Where (ctx.Orders.Include("OrderItems").Where(c => c.OrderID == id).FirstOrDefault()) will not be executed until it reach to the point that we call FirstOrDefault() function which means the Queryable.Where is then translated into native SQL along with the rest of the query and will be passed to the SQL Server, hence you see the Where clause on the SQL statement which definitely is the desired runtime behavior.

By the way, don't forget to import this namespace to your class file:
using System.Linq.Expressions;



来源:https://stackoverflow.com/questions/3540410/ef-4-0-weird-behaviour-of-firstordefault-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!