Getting an expression from a property and adding it to the expression tree

I tried to simplify this example, since the real code I'm playing with is more complicated. Therefore, while this example may seem silly, carry me. Let's say I'm working with an AdventureWorks database, and I decided that I want to add the Blarg property to the Product table, which returns an expression containing the code that I would like to use in several places:

 public partial class Product { public Expression<Func<Product, string>> Blarg { get { return product => product.ProductModelID.HasValue ? "Blarg?" : "Blarg!"; } } } 

What I want to do is create an expression expression tree, make it get an expression from Product.Blarg and a group by the result. Something like that:

 var productParameter = Expression.Parameter(typeof(Product), "product"); // The Problem var groupExpression = Expression.Lambda<Func<Product, string>>( Expression.Invoke( Expression.Property(productParameter, "Blarg"), productParameter), productParameter); using (AdventureWorksDataContext db = new AdventureWorksDataContext()) { var result = db.Products.GroupBy(groupExpression).ToList(); // Throws ArgumentException: "The argument 'value' was the wrong type. // Expected 'System.Delegate'. // Actual 'System.Linq.Expressions.Expression`1[System.Func`2[LINQ_Test.Product,System.String]]'." } 

Obviously, groupExpression is wrong (see the comment for an exception), but I'm not sure how I should do it. What I was thinking about is "get the expression from product.Blarg , execute it and return the result of the string." I think that is not what I actually say there. I'm still trying to figure out expression trees. Any idea how I can take this off?

+4
source share
2 answers

When you call Expression.Invoke , the first argument must be an existing LambdaExpression - it cannot be Expression before a LambdaExpression . Or in other words: he is not going to evaluate Product.Blarg for each line and uses different subexpressions each time.

Instead, you should first extract this lambda, perhaps make it static and access it through reflection if you only know it by name:

 var lambda = (LambdaExpression) typeof(Product) .GetProperty("Blarg").GetValue(null,null); 

And pass lambda as an argument to Expression.Invoke ; here is an example of a fully working LINQ-to-Objects example (via AsQueryable() ):

 using System; using System.Linq; using System.Linq.Expressions; public partial class Product { public static Expression<Func<Product, string>> Blarg { get { return product => product.ProductModelID.HasValue ? "Blarg?" : "Blarg!"; } } public int? ProductModelID { get; set; } static void Main() { var lambda = (LambdaExpression)typeof(Product) .GetProperty("Blarg").GetValue(null, null); var productParameter = Expression.Parameter(typeof(Product), "product"); // The Problem var groupExpression = Expression.Lambda<Func<Product, string>>( Expression.Invoke( lambda, productParameter), productParameter); var data = new[] { new Product { ProductModelID = 123}, new Product { ProductModelID = null}, new Product { ProductModelID = 456}, }; var qry = data.AsQueryable().GroupBy(groupExpression).ToList(); } } 
+3
source
 var qry = data.AsQueryable().GroupBy(Blarg).ToList(); 

This works the same as Marc code.

Note: Blarg already correct, there is no reason to call it again.

+3
source

All Articles