Disabling Linq

time to read 3 min | 578 words

For a lot of things, Linq is really nice. But there is one very important problem with Linq. It make it very easy to do some really stupid things, because it is so easy to intermix in memory operations with remote operations. One of the most common problems that we run into with the RavenDB API was this:

session.LuceneQuery<User>("Users/ByName")
            .Where(x=>x.Name == username)
            .FirstOrDefault();

The problem is that LuceneQuery is not the Linq endpoint to RavenDB. It is the lower level API that we use to query RavenDB, and the Linq API is built on top of that. The problem is that it is very easy to write code like that, and it works, for small amounts of data. What this code actually does is load the first page of information from the database and do the filtering in memory.

Obviously, this is not ideal, but this is a pretty common mistake. We are currently thinking of ways to drastically simplify the API, but in the meantime, something had to be done about this.

LuceneQuery returns an instance of IDocumentQuery:

public interface IDocumentQuery<T> : IEnumerable<T>

That was good, because that meant that we had a hook to place stop the user from making this mistake. The way Linq works, it first looks for an instance method and only afterward it will look for extension methods. That allowed me to define the following:

[Obsolete(@"
You cannot issue an in memory filter - such as Where(x=>x.Name == ""Ayende"") - on IDocumentQuery. 
This is likely a bug, because this will execute the filter in memory, rather than in RavenDB.
Consider using session.Query<T>() instead of session.LuceneQuery<T>. The session.Query<T>() method fully supports Linq queries, while session.LuceneQuery<T>() is intended for lower level API access.
If you really want to do in memory filtering on the data returned from the query, you can use: session.LuceneQuery<T>().ToList().Where(x=>x.Name == ""Ayende"")
", true)]
IEnumerable<T> Where(Func<T, bool> predicate);

Now, the code above it going to generate a very descriptive compilation error, and the entire class of bugs just went out the window.