I have tried to look for a difference quite a few times and there are multiple answers.
One of the common known difference is that IEnumerable
filters data from within memory while IQueryable
does it on the server side.
What does this mean exactly?
Isn't filtering occurring on in-memory data still a server side thing?
I have tried to look for their use in multiple places but I haven't been able to find it in simple words.
Thank you.
IEnumerable<T>
represents something which produces a sequence of results. However, it doesn't expose any information about how the sequence is produced.
IQueryable<T>
exposes the information about how the sequence is to be produced, at the Expression
property, in the form of an expression tree. This information can then be easily mapped to a different set of instructions.
If you call Enumerable.Where
on an IEnumerable<T>
, you're passing in a compiled method compatible with Func<T, bool>
. In theory, we could parse the IL of the compiled method to figure out what the method does, and use that to map to another set of instructions; but that's very complex. Inevitably, the only way to work with this is to load all the objects into memory from the server/provider/datasource, and apply the compiled method on each object.
If you call Queryable.Where
on an IQueryable<T>
, you're passing in an object which by definition represents different code operations -- Expression<Func<T, bool>>
. For example:
IQueryable<Person> qry = /* ... */;
qry = qry.Where(x => x.LastName.StartsWith("A"));
the compiler converts x => x.LastName.StartsWith("A")
to an object representing its various parts:
Lambda expression, returning a `bool`
with an `x` parameter of type `Person`
Call the `StartsWith` method, passing in a constant string `"A"`, on
Result of the `LastName` property, of
the `x` element
More, calling Queryable.Where
itself also modifies the underlying expression tree:
Call to Queryable.Where, passing in
The object at `qry`, and
The previous lambda expression (see above)
When the query is enumerated (either with a foreach
, or a call to ToList
, or something similar), the information can easily be mapped from this object into another form, such as SQL:
SELECT *
FROM Persons
WHERE LastName LIKE N'A%'
or a web request:
http://example.com/Person?lastname[0]=a
The final expression tree after calling Queryable.Where
will look something like this object graph, if it were possible to construct expression trees using constructors and object and collection initializers:
var x = new ParameterExpression {
Type = typeof(Person),
Name = "x"
};
new MethodCallExpression {
Type = typeof(IQueryable<Person>),
Arguments = new ReadOnlyCollection<Expression> {
new ConstantExpression {
Type = typeof(EnumerableQuery<Person>)
},
new UnaryExpression {
NodeType = ExpressionType.Quote,
Type = typeof(Expression<Func<Person, bool>>),
Operand = new Expression<Func<Person, bool>> {
NodeType = ExpressionType.Lambda,
Type = typeof(Func<Person, bool>),
Parameters = new ReadOnlyCollection<ParameterExpression> {
x
},
Body = new MethodCallExpression {
Type = typeof(bool),
Object = new MemberExpression {
Type = typeof(string),
Expression = x,
Member = typeof(Person).GetProperty("LastName")
},
Arguments = new ReadOnlyCollection<Expression> {
new ConstantExpression {
Type = typeof(string),
Value = "A"
}
},
Method = typeof(string).GetMethod("StartsWith", new[] { typeof(string) })
},
ReturnType = typeof(bool)
}
}
},
Method = typeof(Queryable).GetMethod("Where", new[] { typeof(IQueryable<Person>), typeof(Expression<Func<Person, bool>>) })
}
(NB. This was written using the ExpressionTreeToString library. Disclaimer: I am the author.)