使用表达式树的LINQ动态查询



我正在努力熟悉表达式树,但我遇到了困难。我希望能够动态地创建LINQ到XML查询,所以我正在努力熟悉表达式树。我从一个简单的LINQ到XML语句开始,我希望它能够动态生成:

// sample data
var sampleData = new XElement("Items",
new XElement("Item", new XAttribute("ID", 1)),
new XElement("Item", new XAttribute("ID", 2)),
new XElement("Item", new XAttribute("ID", 3))
);
// simple example using LINQ to XML (hard-coded)
var resultsStatic = from item in sampleData.Elements("Item")
where item.Attribute("ID").Value == "2"
select item;
// trying to recreate the above dynamically using expression trees
IQueryable<XElement> queryableData = sampleData.Elements("Item").AsQueryable<XElement>();
ParameterExpression alias = Expression.Parameter(typeof(XElement), "item");
MethodInfo attributeMethod = typeof(XElement).GetMethod("Attribute", new Type[] { typeof(XName) });
PropertyInfo valueProperty = typeof(XAttribute).GetProperty("Value");
ParameterExpression attributeParam = Expression.Parameter(typeof(XName), "ID");
Expression methodCall = Expression.Call(alias, attributeMethod, new Expression[] { attributeParam });
Expression propertyAccessor = Expression.Property(methodCall, valueProperty);
Expression right = Expression.Constant("2");
Expression equalityComparison = Expression.Equal(propertyAccessor, right);
var resultsDynamic = queryableData.Provider.CreateQuery(equalityComparison);

调用CreateQuery时收到的错误是"参数表达式无效"。equityComparison的调试视图显示"(.Call$item.Attribute($ID)).Value=="2"。有人能认出我做错了什么吗?

要更好地了解发生了什么,请始终从所需查询的方法语法开始。在您的情况下,它如下(我特别包括类型,尽管通常我会使用var):

IQueryable<XElement> queryableData = sampleData.Elements("Item").AsQueryable();
IQueryable<XElement> queryStatic = queryableData
.Where((XElement item) => item.Attribute("ID").Value == "2");

现在让我们看看你有什么。

首先,attributeParam变量

ParameterExpression attributeParam = Expression.Parameter(typeof(XName), "ID");

从静态查询中可以看到,属性名称没有lambda参数——唯一支持(也是必需的)参数是item(在代码中由alias变量表示)。因此,这应该是值为"ID"的XName类型的ConstantExpression

var attributeParam = Expression.Constant((XName)"ID");

第二,equalityComparison变量。它所包含的只是item.Attribute("ID").Value == "2"表达式。但是Where方法需要Expression<Func<XElement, bool>>,所以您必须使用equalityComparison作为主体和alias作为参数来创建这样的方法:

var predicate = Expression.Lambda<Func<XElement, bool>>(equalityComparison, alias);

最后,您必须调用Where方法。你可以直接这样做:

var queryDynamic = queryableData.Where(predicate);

或动态:

var whereCall = Expression.Call(
typeof(Queryable), "Where", new Type[] { queryableData.ElementType },
queryableData.Expression, Expression.Quote(predicate));
var queryDynamic = queryableData.Provider.CreateQuery(whereCall);

您可以查看所使用的Expression方法文档,以了解它们的作用

Linq to XML在内存中工作,这意味着您不需要使用表达式树,只需要使用Enumerable扩展方法。使您的代码更加简单易读!!!

最新更新