c# – 自定义IQueryProvider,它依赖于LinqToObjects
作者:互联网
我编写了一个自定义的IQueryProvider类,它接受一个表达式并对SQL数据库进行分析(我知道我可以使用Linq2Sql但是我需要一些修改和调整,不幸的是让Linq2Sql不合适).该类将使用标记的属性(使用属性)识别并执行某些操作,但是我希望能够将表达式传递给LinqToObject提供程序并允许它在之后过滤结果.
例如,假设我有以下linq表达式:
var parents=Context.Parents
.Where(parent=>parent.Name.Contains("T") && parent.Age>18);
Parents类是一个实现IQueryProvider和IQueryable接口的自定义类,但只有Age属性被标记为要检索,因此将处理Age属性,但忽略Name属性,因为它未标记.在我完成处理Age属性之后,我想将整个表达式传递给LinqToObjects来处理和过滤,但我不知道如何.
注:它不需要删除表达式的Age子句,因为即使在我处理它之后结果也是一样的,所以我总是能够将整个表达式发送到LinqToObjects.
我尝试了以下代码,但它似乎不起作用:
IEnumerator IEnumerable.GetEnumerator() {
if(this.expression != null && !this.isEnumerating) {
this.isEnumerating = true;
var queryable=this.ToList().AsQueryable();
var query = queryable.Provider.CreateQuery(this.expression);
return query.GetEnumerator();
}
return this;
}
this.isEnumerating只是一个布尔标志,用于防止递归.
this.expression包含以下内容:
{value(namespace.Parents`1[namespace.Child]).Where(parent => ((parent.Name.EndsWith("T") AndAlso parent.Name.StartsWith("M")) AndAlso (parent.Test > 0)))}
当我逐步完成代码时,尽管将结果转换为列表,它仍然使用我的自定义类进行查询.所以我认为因为类Parent在表达式的开头,它仍然将查询路由回我的提供者,所以我尝试将this.expression设置为方法调用的Argument [1],所以它是这样的:
{parent => ((parent.Name.EndsWith("T") AndAlso parent.Name.StartsWith("M")) AndAlso (parent.Test > 0))}
对我来说看起来更像它,但是,每当我将它传递给CreateQuery函数时,我都会收到此错误’参数表达式无效’.
表达式的节点类型现在是“引用”而不是“调用”,方法为空.我怀疑我只需要以某种方式使这个表达式成为一个调用表达式,它会起作用,但我不知道该怎么做.
请记住,这个表达式是一个where子句,但它可能是任何类型的表达式,我不想在将表达式传递给List查询提供程序之前尝试分析表达式以查看它是什么类型.
也许有一种方法可以使用列表提供程序类剥离或替换原始表达式的Parent类,但仍然可以将其保留在可以作为表达式传递到List提供程序的状态,而不管表达式的类型如何?
任何有关这方面的帮助将不胜感激!
解决方法:
你真是太近了!
我的目标是避免必须“复制”完整的令人费解的SQL-to-Object表达式功能集.你把我放在正确的轨道上(谢谢!)这里是如何在自定义IQueryable中捎带SQL-to-Object:
public IEnumerator<T> GetEnumerator() {
// For my case (a custom object-oriented database engine) I still
// have an IQueryProvider which builds a "subset" of objects each populated
// with only "required" fields, as extracted from the expression. IDs,
// dates, particular strings, what have you. This is "cheap" because it
// has an indexing system as well.
var en = ((IEnumerable<T>)this.provider.Execute(this.expression));
// Copy your internal objects into a list.
var ar = new List<T>(en);
var queryable = ar.AsQueryable<T>();
// This is where we went wrong:
// queryable.Provider.CreateQuery(this.expression);
// We can't re-reference the original expression because it will loop
// right back on our custom IQueryable<>. Instead, swap out the first
// argument with the List's queryable:
var mc = (MethodCallExpression)this.expression;
var exp = Expression.Call(mc.Method,
Expression.Constant(queryable),
mc.Arguments[1]);
// Now the CLR can do all of the heavy lifting
var query = queryable.Provider.CreateQuery<T>(exp);
return query.GetEnumerator();
}
不敢相信这花了3天时间弄清楚如何避免重新发明LINQ-to-Object查询.
标签:c,iqueryable,linq-to-objects 来源: https://codeday.me/bug/20190630/1332358.html