So you might be thinking this is a bad approach from the start. That this sort of functionality belongs on the DAL or at least in a Repository. However, in this age of Microservices and in the context of complex applications, Business logic will exist in several layers. Think of it in MVVM or N-Tier style where there are complex validations and business logic that runs faster when closer to the DAL in a multi-tier environment. In this particular instance I am exposing this sort of module via oData and as an Actor via. Azure Service Fabric.
Getting the internals right is particularly important for me because I am using extensive code generation via T4. If I get it right, it quickly extends to my whole infrastructure of microservices.
Early on I thought I could implement just parts of IQueryable and be able to get away with it. I tried only implementing Where(), Skip() and Take() without the host of classes needed for an actual IQueryable. This worked great when my code is loosely coupled to the implementation and blindly executes only these operations.
The catch is that I couldn’t just cast a partial implementation to IQueryable for things like WebApi to use. It would be great to just implement these three operations and have some sort of generic implementation of a decorator that bridges the implementation to an IQueryable. Alas, there is no such bridge in native .NET. Thus, we must help ourselves.
Poking around the web you will find an ancient Microsoft secret Walkthrough: Creating an IQueryable LINQ Provider. Many posts about this subject fail to address the fact that you may not be using any sort of IQueryable under the hood. The MSDN post shows you how without directly addressing it directly.
At a high level: during the Execute() phase you will need to figure out what you can pass on, do so, and then execute the underlying query to return your list. This list then becomes the subject of the remainder of the query.
The following post will walk through my implementation thought process.