A while ago, I announced Linq to Amazon and started to describe how it's implemented. Actually producing some content for the book and summer holidays kept me busy for a while, but here is finally the last part of this series of posts.
In the previous post, I said that what had to be done to create the Linq extension is to implement the IQueryable<T> interface, and write the code that converts an expression tree into an Amazon query.
What I have done is just a quick and dirty implementation. The goal is not to provide a complete solution to query Amazon using Linq, but instead to create an example to give you an idea of how the Linq extensibility stuff works. As you will see by looking at the source code, a lot of work would be required for a complete implementation.
Let's describe what you'll find in the source code.
The BookSearch class implements IQueryable<Book>. This interface speaks for itself. An object that implements it can be queried for Book objects! A BookSearch instance is what we'll query. Our queries start with code like the following:
var query =
from book in new Amazon.BookSearch()
The important method to look for in the BookSearch class is CreateQuery<S>. This method receives an argument of type Expression. This is our expression tree. All that the CreateQuery<S> method does is creating and returning a new instance of the BookQuery class, passing the expression tree to its constructor.
The BookQuery class also implements IQueryable<Book>, but doesn't handle the same methods. First, an important thing to understand is that a BookQuery object keeps the expression tree it receives in its constructor as a private field. This will allow the BookQuery object to analyze the expression later on, when needed. The key point here is that a BookQuery instance represents a given query, which is defined by the expression tree. A different query produces a different expression tree of course.
The method to look at in the BookQuery class is IEnumerable.GetEnumerator. This is where everything happens: we parse the expression tree, we convert the Linq query into a web query, perform the web query, create a collection of the resulting books and return the book enumeration.
The other pieces of code you'll want to look at are the methods that visit the expression tree to extract the query criteria (the ProcessXXX methods), and the method that performs the web query using Linq to XML (the PerformWebQuery method).
I won't provide you with more details at this point. The source code (see below) is free for you to look at. Of course feel free to ask any question, and I'll do my best to help you.
Again, this is a straightforward implementation. This implementation supports only simple queries and is likely to fail if you try to use it with different queries... This code has been written without help from Microsoft. There is no documentation on expression trees or on how to implement IQueryable.
By the time the Linq in Action book is out, we should have more information from Microsoft and I'll adapt this sample as needed.
In the meantime, feel free to play with Linq's extensibility and to give a try to creating your own implementations! I'd be really happy if you let me know what you are up to :-)
Linq to Amazon source code => The source code has been updated for .NET 3.5 and VS 2008. It is available for download as part of the complete source code in C# and VB for the LINQ in Action book.
Note: In order to use the Amazon.com web services and test this example fully, you need to register with the Amazon Web Services program. After registering with Amazon you will be assigned an access key. Edit the BookQuery.cs file and replace INSERT YOUR AWS ACCESS KEY HERE by your access key.