Search code examples
c#.netienumerableiqueryable

How does the behavior of .Take() changes based on the interface reference I'm using on left. IQueryable vs IEnumerable


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.


Solution

  • 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.