I'm building an expression tree dependency analyzer for a cross data source IQueryProvider.
That is, I have an IQueryable with some elements that can be executed locally in memory against some arbitrary provider (say Entity Framework). Some other elements in the IQueryable go against an entity that I need to make a remote WCF call. The WCF operation takes a serialized expression tree, will deserialize it, execute the LINQ query against its own local data store (lets also say Entity Framework), then send me back the results (though this mechanism could just as easily be a WCF Data Services DataServiceQuery...but I'm not using it because it's level of functional support is limited...at best). Once I get the results back from the WCF service, I will perform the result of the LINQ query in memory against the locally executed LINQ query.
So, what's so hard about that? Well, I need to determine the dependencies of the expression tree, so that my local underlying Query Provider won't explode trying to execute my LINQ query which has components that can only be executed on the remote WCF service...and vice versa.
Let's take the simple scenario:
var result =
(from entityX in new Query<MyEntityX>()
from entityY in new Query<MyEntityY>()
where entityX.SomeProperty == "Hello" &&
entityY.SomeOtherProperty == "Hello 2" && entityX.Id == entityY.XId).ToList();
Query<T>
is a simple queryable wrapper with my own provider which has the chance to parse the tree an figure out what to do before swapping out roots with a different query provider. So, in the above case I need to:
Execute the query against MyEntityA using a local object context and apply only the
myEntityX.SomeProperty == "Hello"
criteria. That is, run the following locally:// assume the functionality for replacing new Query<MyEntityA> with new
// ObjectContext<MyEntityA>() is already there...
var resultX = (from entityX in new Query<MyEntityX>()
where entityX.SomeProperty == "Hello").ToList().AsQueryable();Send over the following serialized and have it execute on my remote WCF service, then get the results back.
// Send the preceeding expression over the over the wire
// and get the results back (just take my word this already works)
var resultY = (from entityY in new Query<MyEntityY>()
where entityY.SomeOtherProperty == "Hello 2").ToList().AsQueryable();Execute the following in memory:
var finalResult = (from entityX in resultX
from entityY in resultY
where entityX.SomeProperty == "Hello" &&
entityY.SomeOtherProperty == "Hello 2" &&
entityX.Id == entityY.XId).ToList();
Note that the solution must incorporate a way of accumulating criteria that is specified off of projections too...like
var result =
(from i in
(from entityX in new Query<MyEntityX>()
from entityY in new Query<MyEntityY>()
select new { PropX = entityX, PropY = entityY })
where
i.PropX.SomeProperty == "Hello" && i.PropY.SomeOtherProperty == "Hello 2"
&& i.PropX.Id == i.PropY.XId
select i)
.ToList();
This should result in the same two individual queries above being actually issued before the rest is evaluated in memory. On an unrelated note, I think I will probably used PLINQ and or DRYAD for running the in memory operations with improved performance.
So, I have some ideas (like doing some passes over the tree with a visitor and accumulating candidates for a given entity type), but I'm looking for some other peoples' suggestions about how to accumulate the parts my expression tree that can be executed against a given context...that is, knowing that one where criteria applies to one underlying new Query<T>
and another criteria applies to a different one...so that I can figure out what I can do against data store 1, what I can do against data store 2 and what I need to do in memory, and execute the different parts of the tree accordingly. It's sort of like a Funcletizer, but a bit more complex...
Thanks for any assistance.