Finally doing something
In the previous post of this series, I introduced the first classes implied in setting-up the Linq provider : QueryableDummyData and DummyQueryProvider, but these classes weren’t doing much of the real work. We were finally getting to the DummyQueryContext class… which I said was going to actually do something.
DummyQueryContext is an internal class, which exposes a single method :
internal static object Execute(Expression expression, bool isEnumerable)
This method’s goal is to take an expression tree as its input, and to return the expected results for the query expression. Up to this point, the calling chain looks like this :
The first implementation on the Execute method is then :
internal static object Execute(Expression expression, bool isEnumerable) { // First, we call the web service and get an array of persons IEnumerable<Person> persons; using (DummyService.PeopleFinderClient service = new DummyService.PeopleFinderClient()) { persons = service.FindPeople(new DummyService.SearchCriteria()); } // We use Ling to Objects to get an IQueryable instance of persons var queryablePersons = persons.AsQueryable(); // We transform the expression tree Expression finalExpressionTree = ExpressionTreeConstantReplacer .CopyAndReplace( expression, typeof(QueryableDummyData<Person>), queryablePersons); // Finally, based on the new tree, we either create a query or // execute with the Linq to Objects provider IQueryProvider provider = queryablePersons.Provider; if (isEnumerable) return provider.CreateQuery(finalExpressionTree); else return provider.Execute(finalExpressionTree); }
Remember : my test case is only to get an enumerator and check that we can MoveNext. In order to make the test pass, we don’t have to worry about filtering or sorting in a any way, so the previous implementations calls the service with no particular parameter, and the service returns all its data.
For now, the trickiest part is the expression tree transformation. Its goal is to build a new expression tree where the QueryableDummyData<Person> instance is replaced with the Person[] array returned by the service. Once this replacement done, the Execute method will let the Linq to Objects provider run the query against the array. The isEnumerable is used to know whether the method must return a scalar value (via the queryablePersons.Provider.Execute method) or an IEnumerable (via the queryablePersons.Provider.CreateQuery method). For our first test, we want to get an IEnumerable<Person>.
The manipulated expression tree is built upon the following code snippet :
var queryablePersons = new QueryableDummyData<Person>();
Here is a representation of the generated expression tree :
And a representation of the final expression tree, once the QueryableDummyData<Person> has been replaced by the Person[] array retrieved from the service :
Next time, we’ll talk about the visitor pattern, and see how the ExpressionTreeModifier actually makes the replacement.