LINQ Dynamic Query using Expression Tree

c# expression-trees linq

Question

I am trying to familiarize myself with Expression Trees, and I'm hitting a wall. I want to be able to dynamically create LINQ to XML queries, so I am trying to familiarize myself with Expression Trees. I started with a simple LINQ to XML statement that I want to be able to generate dynamically:

        // 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);

The error that I get when calling CreateQuery is 'Argument expression is not valid'. The debug view for equalityComparison shows '(.Call $item.Attribute($ID)).Value == "2"'. Can someone identify what I am doing incorrectly?

1
1
1/12/2017 8:35:57 PM

Accepted Answer

To better understand what's going on, always start with method syntax of the desired query. In your case it is as follows (I'm specifically including the types although normally I would use var):

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

Now let see what you have.

First, the attributeParam variable

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

As you can see from the static query, there is no lambda parameter for attribute name - the only supported (and required) parameter is the item (which in your code is represented by the alias variable). So this should be ConstantExpression of type XName with value "ID":

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

Second, the equalityComparison variable. All it contains is the item.Attribute("ID").Value == "2" expression. But Where method requires Expression<Func<XElement, bool>>, so you have to create such using the equalityComparison as body and alias as parameter:

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

Finally you have to call Where method. You can do that directly:

var queryDynamic = queryableData.Where(predicate);

or dynamically:

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

You can take a look at the used Expression methods documentation for further details what they do.

2
1/12/2017 9:18:27 PM

Popular Answer

Linq to XML works in memory, meaning you do not nead Expression trees, just use the Enumerable extension methods. Makes your code much simplier and easier to read!!!



Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow