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

时间:2017-01-12 20:35:57

标签: c# linq expression-trees

我正在努力熟悉表情树,而且我正在撞墙。我希望能够动态创建LINQ to XML查询,所以我想让自己熟悉Expression Trees。我从一个简单的LINQ to 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时出现的错误是&#39;参数表达式无效&#39;。 equalityComparison的调试视图显示&#39;(。调用$ item.Attribute($ ID))。值==&#34; 2&#34;&#39;。有人可以识别我做错了什么吗?

2 个答案:

答案 0 :(得分: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表示变量)。因此,ConstantExpression类型为XName,值为&#34; ID&#34;:

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方法文档,了解它们的详细信息。

答案 1 :(得分:0)

Linq to XML在内存中工作,意味着你没有表达树,只需使用Enumerable扩展方法。使您的代码更简单,更易于阅读!!!