1

Say I have the following query how can I get the base Expression Tree?

MyContext.Items.Select(i=>i.Color);

I want to get the expression tree of this so that I can then use the expression tree to dynamically set what property is being selected (So I can choose Color, Size, Weight, Price etc.)

I think I am close with the following but I keep getting errors:

        IQueryable<Item> query = c.Items;


        string SelctField = "Color";
        ParameterExpression pe = Expression.Parameter(typeof(Item), "i");
        Expression SelectProperty = Expression.Property(pe, SelctField);

        MethodCallExpression Select = Expression.Call(
            typeof(Queryable),
            "Select",
            new Type[] {query.ElementType},
            pe,
            Expression.Lambda<Func<Item, string>>(SelectProperty, pe));

        var result =  query.Provider.CreateQuery<string>(Select);

The above results in

No generic method 'Select' on type 'System.Linq.Queryable' is compatible with the
supplied type arguments and arguments. No type arguments should be provided if the 
method is non-generic

so I tried removing that overload and just keep getting different errors.

I do also get this internal expression text in debug mode, but don't understand how to convert it.

    .Call System.Linq.Queryable.Select(
    .Call .Constant<System.Data.Entity.Core.Objects.ObjectQuery`1[Inventory_Ordering_And_Reporting.Item]>(System.Data.Entity.Core.Objects.ObjectQuery`1[Inventory_Ordering_And_Reporting.Item]).MergeAs(.Constant<System.Data.Entity.Core.Objects.MergeOption>(AppendOnly))
    ,
    '(.Lambda #Lambda1<System.Func`2[Inventory_Ordering_And_Reporting.Item,System.Nullable`1[System.String]]>))

    .Lambda      #Lambda1<System.Func`2[Inventory_Ordering_And_Reporting.Item,System.Nullable`1[System.String]]>(Inventory_Ordering_And_Reporting.Item $i)
{
    $i.Color
}
user2125348
  • 430
  • 1
  • 7
  • 21
  • 1
    I might suggest having a look at this: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx and this http://dynamiclinq.codeplex.com/ – AaronLS Apr 25 '14 at 18:45
  • @AaronLS All they do is use expression trees anyways, for this perpose adding another dependency is overkill, especially when I can just use the exact same method they use anyways (at a much smaller scale) – user2125348 Apr 25 '14 at 18:46
  • I was more thinking you could look at the source of the Dynamic.cs and see how they did this programmatically. There's an article specifically about expression trees here: http://msdn.microsoft.com/en-us/library/bb882637.aspx – AaronLS Apr 25 '14 at 19:28
  • 1
    @AaronLS I have both looked at how they did it and also read your link article, a few times. And I still just can't get it right. I have added an attempt of my to my question. But I still can't wrapo my head around it. – user2125348 Apr 25 '14 at 19:31

2 Answers2

2

Not entirely sure if this will work with DB provider, but hopefully since it's purely expression based.

public class Item
{
    public string Blah { get; set; }
    public string Foo { get; set; }
}

[TestMethod]
public void Test()
{
    string propertyName = "Blah";
    System.Linq.Expressions.ParameterExpression arg = System.Linq.Expressions.Expression.Parameter(typeof(Item), "x");

    PropertyInfo pi = typeof(Item).GetProperty(propertyName,
    BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
    System.Linq.Expressions.Expression expr = System.Linq.Expressions.Expression.Property(arg, pi);
    System.Linq.Expressions.LambdaExpression lambda = System.Linq.Expressions.Expression.Lambda(expr, arg);
    System.Linq.Expressions.Expression<Func<Item, string>> funcExpression = (System.Linq.Expressions.Expression<Func<Item, string>>)lambda;

    List<Item> test = new List<Item> { new Item { Blah = "Test", Foo = "Another" } };
    IQueryable<Item> query = test.AsQueryable();
    var result = query.Select(funcExpression).ToList();
}
AaronLS
  • 37,329
  • 20
  • 143
  • 202
  • This works, is there a reason for the PropertyInfo part though? As when I run without that and replace your `pi` with the string containing the property name I want (as in my original example) it works. – user2125348 Apr 25 '14 at 20:31
  • Yeh, you are utilizing a different overload of .Property. I honestly don't know if there is any benefit to using PropertyInfo, maybe in cases where you want more control over what properties are allowed to be dynamically selected(i.e. preventing a string containing the name of a private property). – AaronLS Apr 25 '14 at 21:17
  • Hopefully an easy solution, for your line `System.Linq.Expressions.Expression> funcExpression = (System.Linq.Expressions.Expression>)lambda;` is there any way to set the `string` portion of `Func` to be `pi.PropertyType` ?? – user2125348 May 12 '14 at 16:47
1

You could use reflection:

    public static void DoStuff(string fieldName)
    {
        List<test> ban = new List<test>();
        ban.Add(new test() { number = 40, notNumber = "hi" });
        ban.Add(new test() { number = 30, notNumber = "bye" });


        var result = ban.Select(item => item.GetType().GetField(fieldName).GetValue(item));
        foreach (var item in result)
        {
            Console.WriteLine(item);
        }
    }
    class test
    {
        public int number;
        public string notNumber;
    }

This example is for fields the select should probably check to see if the field exists and if not then look for a property.

Delta
  • 869
  • 6
  • 12
  • You can't use GetType, GetField, or GetValue in LINQ to Entities, this was the first thing I tried when I sought after the dynamic Select, but LINQ to Entities does not recognize them – user2125348 Apr 25 '14 at 20:15
  • 1
    This should work for in memory sets. But I have a feeling it won't work for LINQ2SQL or 2Entities or any other DB provider. – AaronLS Apr 25 '14 at 20:16
  • 1
    I think what I found out was that LINQ 2... can only recognize a specific subset of methods, all are contained within [InsertDBNameHere]Functions Class, for example I can only use EntityFunctions Class methods. Only methods that linq can translate into SQL are supported. – user2125348 Apr 25 '14 at 20:20