Tuesday, March 10, 2009

LINQ in DataObjects.Net v4.0: details and issues

I think it's right time to give some details on implementation of LINQ in new DO. Let's start from issues:

1. Correlated subqueries. LINQ allows us to write generally anything in from clause (collectionSelector expression in SelectMany) - e.g. a correlated subquery: from a in MainQuery from b in Subquery select new {a, b} (translated by C# to MainQuery.SelectMany(a => Subquery, (a, b) => new { a = a, b = b } )). Note that Subquery is generally any IQueryable, including e.g. AnotherQuery.Where(a.X==...).

Standard SQL doesn't allow to do the same - you can't use correlated subqueries in from clause there. Quite strange, because generally this doesn't significantly affect on anything. But:
- SQL Server 2005 supports this via Cross / Outer apply statement.
- We also suppport this in RSE by ApplyProvider.

It's easy to implement its executable counterpart, but its translation to SQL is rather tricky: we've added this statement to SqlDom, although now we can complile it only for SQL Server 2005. We hope it will be possible to do this without serious limitations for Oracle and PostgreSQL as well, although we aren't going to rely on this: we'll translate correlated subqueries (actually this is possible for the most frequently used part of them) to RSE using JoinProvider or PredicateJoinProvider - in fact, we expect correlated subquery looks like NonCorrelatedSubquery.Where(correlatedExpression) in this case. In other cases we'll use ApplyProvider.

Btw, EF and LinqToSql behave the same (nearly - e.g. there is no RSE behind them ;) ).

2. DateTime and TimeSpan handling. Unfortunately, SQL Server doesn't support interval (or similar) data type, but many others do, so functions dealing with dates are quite different. We did a lot to handle this:
- First of all, we implemented corresponding abstraction layer in SqlDom.
- Secondly, we implemented unified method call translation framework - so-called MemberCompilerProvider - a base class in Xtensive.Core.Linq providing compilers (methods) for specified members and allowing to invoke them fast during the translation process. Now it allows us (and you!) to define compilers operating at any desirable translation layer (in this case, at RSE to SQL translation layer) for generally any invoked members. See sources in Xtensive.Storage.Providers.Sql.Mappings.FunctionMappings namespace (internal!) for many many examples. Here is just one:

[Compiler(typeof(DateTime), "Year", TargetKind.PropertyGet)]
public static SqlExpression DateTimeYear(SqlExpression this_)
return SqlFactory.Extract(SqlDateTimePart.Year, this_);

... To be continued.

No comments:

Post a Comment