Reputation: 361
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?
Upvotes: 1
Views: 973
Reputation: 1140
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!!!
Upvotes: 0
Reputation: 205849
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.
Upvotes: 2