1

I am trying to apply additional server-side filtering to entities exposed by an asp.net OData service, using Linq to Entities.

So far so good, but the Linq query that I build for the filter can sometimes get to an enormous length, causing a StackOverflowException.

As a workaround, I want to execute the query in a separate thread, giving that thread a bigger stack size limit. To achieve this, I implemented an IQueryable and IQueryProvider wrappers, so that when the IQueryable's Execute method is called, it would be executed in a separate thread.

To keep it simple and focused on my problem, I will omit the threading part in the following code samples, as this is not the issue here.

Here's the relevant parts of the code:

public class MyQueryable<T> : IQueryable<T> {
    private IQueryable<T> _Inner { get; set; }
    private IQueryProvider _Provider { get; set; }

    public MyQueryable(IQueryable<T> inner) {
        _Inner = inner;
        _Provider = new MyQueryProvider(inner.Provider);
    }

    public Type ElementType {
        get { return _Inner.ElementType; }
    }

    public Expression Expression {
        get { return _Inner.Expression; }
    }

    public IQueryProvider {
        get { return _Provider; }
    }

    /* ... Implementations of GetEnumerator ... */
}

public class MyQueryProvider : IQueryProvider {
    private IQueryProvider _Inner { get; set; }

    public MyQueryProvider(IQueryProvider inner) {
        _Inner = inner;
    }

    public IQueryable<T> CreateQuery<T>(Expression expression) {
        return new MyQueryable<T>(_Inner.CreateQuery<T>(expression));
    }

    public T Execute<T>(Expression expression) {
        // The problem occurs on the following line.
        return _Inner.Execute<T>(expression);
    }

    /* ... Implementation of the non-generic versions of CreateQuery, Execute ... */
}

When I run this, I get an InvalidOperationException in MyQueryProvider.Execute method:

Cannot compare elements of type 'System.Collections.Generic.ICollection`1'.
Only primitive types (such as Int32, String, and Guid) and entity types are supported.

This is how I build the original IQueryable that is being passed to MyQueryable: I get the DbSet from my entities context, apply OData filter query option on it, and call Count(). Something along the lines of:

((odataQueryOptions.Filter.ApplyTo(
    context.Entities.AsQueryable(), new ODataQuerySettings()))
as IQueryable<Entity>).Count()

I partially identified that the problem is that the odata filter query option contains a nested 'any' filter, something like:

Entities/$filter=RelatedEntities/any(entity: (entity/ID eq 1))

which adds a check to the IQueryable's Where expression whether the RelatedEntities collection is null. This check is suppostedly what causes the above exception.

What I cannot understand, is why the original IQueryable fails when I try to delegate the Execute method's execution to it from the MyQueryable.Execute method, but it all works fine when I use the original IQueryable directly.

Any help on this would be highly appreciated. Thanks in advance.

Johan Hirsch
  • 557
  • 4
  • 21

0 Answers0