The core of LINQ to Objects is IEnumerable
- Query methods are designed for IEnumerable
as extension methods, like Where(), Select(), etc.; - Query methods are designed to be fluent, LINQ to Objects queries can be written in declarative paradigm via method chaining;
- Query methods are designed to be deferred execution as long as possible.
Since most of the .NET collections implements IEnumerable
By contrast, the core of LINQ to SQL is IQueryable
IQueryable and IQueryable
IQueryable and IQueryable
namespace System.Linq{ public interface IQueryable : IEnumerable { Type ElementType { get; }
Expression Expression { get; }
IQueryProvider Provider { get; } }
public interface IQueryable<out T> : IEnumerable<T>, IQueryable, IEnumerable { }}Check this post for the meaning of out keyword.
The ElementType property is easy to understand. Generally speaking, an IQueryable
- Expression property returns an Expression object to express the meaning of the current query;
- Provider property returns an IQueryProvider, which is able to execute the current query on the specific data source.
The concept of expression and query provider will be covered in later posts. This post will concentrate on IQueryable
IQueryable and IQueryable extensions
Just like a bunch of extension methods for IEnumerable and IEnumerable
| Category | System.Linq.Enumerable | System.Linq.Queryable |
| Restriction | Where, OfType | Where, OfType |
| Projection | Select, SelectMany | Select, SelectMany |
| Ordering | OrderBy, ThenBy, OrderByDescending, ThenByDescending, Reverse | OrderBy, ThenBy, OrderByDescending, ThenByDescending, Reverse |
| Join | Join, GroupJoin | Join, GroupJoin |
| Grouping | GroupBy | GroupBy |
| Aggregation | Aggregate, Count, LongCount, Sum, Min, Max, Average | Aggregate, Count, LongCount, Sum, Min, Max, Average |
| Partitioning | Take, Skip, TakeWhile, SkipWhile | Take, Skip, TakeWhile, SkipWhile |
| Cancatening | Concat | Concat |
| Set | Distinct, Union, Intersect, Except, Zip | Distinct, Union, Intersect, Except, Zip |
| Conversion | ToSequence, ToArray, ToList, ToDictionary, ToLookup, Cast, AsEnumerable | Cast, {AsQueryable} |
| Equality | SequenceEqual | SequenceEqual |
| Elements | First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAt, ElementAtOrDefault, DefaultIfEmpty | First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAt, ElementAtOrDefault, DefaultIfEmpty |
| Generation | [Range], [Repeat], [Empty] | |
| Qualifiers | Any, All, Contains | Any, All, Contains |
The underlined methods are extension methods for the non-generic IEnumerbale and IQueryable interfaces. Methods in [] are normal static methods. And the AsQueryable() methods in {} are also special, they are extension methods for IEnumerable and IEnumerable
Please notice that, since IQuerayable
Table
In LINQ to SQL, most of the time, the query works on (the model of) SQL data table:
Table<Product> source = database.Products; // Products table of Northwind database.IQueryable<string> results = source.Where(product => product.Category.CategoryName == "Beverages") .Select(product => product.ProductName);The actual type of (the model of) Products table is Table
[Database(Name = "Northwind")]public partial class NorthwindDataContext : DataContext{ public Table<Product> Products { get { return this.GetTable<Product>(); } }}And, Table
namespace System.Data.Linq{ public sealed class Table<TEntity> : IQueryable<TEntity>, IQueryable, IEnumerable<TEntity>, IEnumerable, ITable<TEntity>, ITable, IQueryProvider, IListSource where TEntity : class { // ... }}So all the above query methods are applicable for Table
IEnumerable extensions vs. IQueryable extensions
In the above table, two kinds of Where() extension methods are applicable for IQueryable
- Where() extension method for IQueryable
, defined in Queryable class; - Where() extension method for IEnumerable
, defind in Queryable class, since IQueryable implements IEnumerable .
They are difference from the signatures:
namespace System.Data.Linq{ public static class Enumerable { // This is also Available for IQueryable<T>, // because IQueryable<T> implements IEnumerable<T>. public static IEnumerable<TSource> Where<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate) { // ... } }
public static class Queryable { public static IQueryable<TSource> Where<TSource>( this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate) { // ... } }}Please notice that the above Where() method invocation satisfies both of the signatures:
source.Where(product => product.Category.CategoryName == "Beverages").Select(...In this invocation:
- source argument: it is a Table
object, and Table implements both IQueryable and IEnumerable ; - predicate argument: The predicate is written as lambda expression, accorsing to this post, lambda expression (product => product.Category.CategoryName == “Beverages”) can be compiled into either anonymous method (Func<Product, bool>) or expression tree (Expression<Func<Product, bool>>).
How does the compiler choose the 2 satisfied Where() methods? Because Queryable.Where()’s first parameter is an IQueryable
Because Queryable.Where() returns an IQueryable
So the above qeury is equal to:
IQueryable<Product> source = database.Products; // Products table of Northwind database.// Queryable.Where() is choosed by compiler.IQueryable<Product> products = source.Where(product => product.Category.CategoryName == "Beverages");// Queryable.Select() is choosed by compiler.IQueryable<string> results = products.Select(product => product.ProductName);By checking all the duplicated extension mrethods betwwen IEnumerable
- replacing all IEnumerable
parameter with IQueryable parameter; - replacing all function parameter with expression tree parameter.
The expression tree parameter will be explained in the next post.