To give a sense of what is shared and what isn’t between LINQ to SQL and LINQ to Entities (both in terms of modules and behavior) I’ll repeat some information that’s probably familiar to most people on this thread…
LINQ providers support integrated query via the IQueryable and IQueryProvider interfaces. The provider’s job is basically to translate LINQ expression trees into native queries. LINQ to SQL and LINQ to Entities accomplish this translation in roughly the same way, though nothing is shared beyond the System.Linq components in System.Core.dll.
Matt Warren’s IQueryable series gives a more detailed overview (http://blogs.msdn.com/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx) but I’ll include the highlights here to provide some context:
1. At compile time, user code is translated into (or directly uses) expression builder calls.
2. At runtime, calls to System.Linq.Queryable methods yield additional expression trees with argument expressions inline.
3. The provider is passed a LINQ expression which it translates into a “query” expression. LINQ to SQL and LINQ to Entities use different internal representations of the expression (a variant of LINQ expressions and Canonical Query Trees respectively). This step includes the replacement of method calls with query operators and the replacement of member accesses with store function calls.
4. Each provider then compensates for non-relational structures in the query (e.g. types, nested collections in results, etc.)
5. Native queries are generated and executed.
6. Query results are reshaped (basically the inverse of step 4).
As I mentioned, there is really nothing shared between LINQ to SQL and LINQ to Entities beyond step 2. The example in this thread illustrates a couple of differences between the two stacks in the handling of step 3 and step 4.
Implicit vs. explicit client evaluation
LINQ to SQL is a hybrid provider in the sense that it supports evaluation of parts of the query in the client and parts in the store. As a result, the following query succeeds in LINQ to SQL but fails in LINQ to Entities:
var query = from o in context.Orders
where GetCutoffDate() > o.OrderDate
select o;
As Brian suggested, it must be rewritten as:
DateTime cutoffDate = GetCutoffDate();
var query = from o in context.Orders
where cutoffDate > o.OrderDate
select o;
This makes the boundary between client and store evaluation explicit. There is a tradeoff between self-containment of queries and predictability of behavior for non-expert users.
Canonical functions vs. store functions
Of course, the sub-expression mentioned in this thread could be entirely translated into Transact-SQL function calls:
DateTime.UtcNow.AddDays(-180)
becomes
DATEADD(day, -180, GETUTCDATE())
Note that although LINQ to SQL could translate the above expression into Transact-SQL, it chooses to evaluate it on the client because it is uncorrelated. The behavior is different depending on whether results are correlated or not.
In Beta 2, LINQ to Entities does not support the DateTime.AddDays method simply because a corresponding “canonical function” does not exist. Canonical functions are store agnostic and can be implemented by arbitrary Entity Frameworks providers. We plan on introducing a few more DateTime-related functions in the next release (the usual disclaimers apply) at which point this expression will be supported by LINQ to Entities.
Regarding Rick’s question: Entity-SQL supports both canonical and store-specific functions. For functions not supported by LINQ to Entities, this provides the cleanest workaround. Where the gap is due to unsupported CLR methods – in either LINQ to SQL or LINQ to Entities – consider explicitly breaking up the query into client- and store- side elements. For instance, to extend the above example:
DateTime cutoffDate = GetCutoffDate();
var query = from o in context.Orders
where cutoffDate > o.OrderDate
select o;
var hybridQuery = from row in query.AsEnumerable()
select ShapeResult(row.Oid, row.Description, row.OrderDate);
Recommendation:
When working with LINQ to Entities, keep in mind that boundaries between client- and store- execution are explicit. To use a value computed on the client within a query, assign the value to a local variable before executing (see the assignment to cutoffDate above). To perform client-side processing of results, use the AsEnumerable method to yield results from the store query and then apply client-side logic (see the assignment to hybridQuery above).
Thanks,
-Colin