我正在尝试使用 Linq to Entities 对 asp.net OData 服务公开的实体应用额外的服务器端过滤。
到目前为止一切都很好,但是我为过滤器构建的 Linq 查询有时会变得很长,从而导致 StackOverflowException。
作为一种解决方法,我想在一个单独的线程中执行查询,为该线程提供更大的堆栈大小限制。为了实现这一点,我实现了 IQueryable 和 IQueryProvider 包装器,这样当调用 IQueryable 的 Execute 方法时,它将在单独的线程中执行。
为了保持简单并专注于我的问题,我将在以下代码示例中省略线程部分,因为这不是这里的问题。
以下是代码的相关部分:
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 ... */
}
当我运行它时,我在 MyQueryProvider.Execute 方法中得到一个 InvalidOperationException:
Cannot compare elements of type 'System.Collections.Generic.ICollection`1'.
Only primitive types (such as Int32, String, and Guid) and entity types are supported.
这就是我构建传递给 MyQueryable 的原始 IQueryable 的方式:我从实体上下文中获取 DbSet,对其应用 OData 过滤器查询选项,然后调用 Count()。类似于以下内容:
((odataQueryOptions.Filter.ApplyTo(
context.Entities.AsQueryable(), new ODataQuerySettings()))
as IQueryable<Entity>).Count()
我部分确定问题在于 odata 过滤器查询选项包含嵌套的“任何”过滤器,例如:
Entities/$filter=RelatedEntities/any(entity: (entity/ID eq 1))
这会向 IQueryable 的 Where 表达式添加检查 RelatedEntities 集合是否为空。据推测,此检查是导致上述异常的原因。
我无法理解的是,为什么当我尝试将 Execute 方法的执行从 MyQueryable.Execute 方法委托给它时,原始 IQueryable 会失败,但是当我直接使用原始 IQueryable 时,它一切正常。
对此的任何帮助将不胜感激。提前致谢。