Assume I've these sample codes
IQueryable<Employee> data = context.Employees.Where(x => x.FullName != "Shekar Reddy");
var topEmp = data.Take(1);
foreach (var item in topEmp)
{
Console.WriteLine(item.FullName);
}
and
IEnumerable<Employee> data = context.Employees.Where(x => x.FullName != "Shekar Reddy");
var topEmp = data.Take(1);
foreach (var item in topEmp)
{
Console.WriteLine(item.FullName);
}
the only difference between these to is the the reference I'm using IQueryable vs IEnumerable. 1st snippet is generating sql query to get top 1 item matching the filter criteria and the later is generating a sql query without top filter.
In both the cases, the object inside the data is of type Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable however the topEmp in the first scenaro is of type : Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable and in second it is System.Linq.Enumerable.EnumerablePartition
How the behavior of the .Take method changes based on the interface reference on the left. I see the "data" varialbe is of same type in both cases. As per my understanding, if the left side reference is a class then .Take of that class could get called; but they are interfaces. I'm sure I'm missing a basic concept of C# here.
You are correct that you've missed a basic concept of C#, and this concept is extension methods.
We are not calling an abstract Take
method implemented by the Enumerable
object.
Instead, we are invoking a function that exists separately to the object: the Take
extension methods. There are different methods based upon the type of object.
Inside the System.Core library exist these static classes.
public static class Queryable
{
public static IQueryable<TSource> Take<TSource>(
this IQueryable<TSource> source,
int count
);
// Many other methods...
}
public static class Enumerable
{
public static IEnumerable<TSource> Take<TSource>(
this IEnumerable<TSource> source,
int count
);
// Many other methods...
}
Extension methods are resolved at compile time, not runtime.
Your data
variable happens to implement IQueryable<T>
. But, once you've cast it to an IEnumerable<T>
, the compiler must choose the IEnumerable<T>
extension method.