2015-06-26 3 views
2

Я изучаю «Expression Tree», но я не удается выполнить эти выражения:перечислимых Выбор по Expression Tree

// first case 
someList.Select(p => p.SomeProperty); 

и

// second case 
someList.Select(p => new OtherClass 
{ 
    SomeProperty = p.SomeProperty 
}) 

К «первый случай» я попытался сделать это:

var someList = new List<SomeClass>(); 
someList.Add(new SomeClass { SomeProperty = "Hello" }); 

var someParam = Expression.Parameter(typeof(SomeClass), "p"); 
var someProperty = Expression.Property(someParam, "SomeProperty"); 

Expression.Call(
    typeof(Enumerable), 
    "Select", 
    new Type[] 
    { 
     typeof(SomeClass), 
     typeof(string) 
    }, 
    Expression.Lambda(
     someProperty, 
     someParam 
    ) 
).Dump(); 

Но я получаю эту ошибку:

InvalidOperationException: Нет универсального метода «Выбрать» в типе «System.Linq.Enumerable» совместим с аргументами и аргументами поставляемого типа. Аргументы типа не должны предоставляться, если метод не является общим.

О «втором случае», у меня нет идей, как действовать.

Может ли кто-нибудь вести меня здесь?

+0

Я думаю, что вам не хватает параметра IEnumerable , который будет находиться между 'new Type []' и 'Expression.Lambda' в' Expression.Call' – Will

+0

. Вы не можете использовать прямые деревья выражений с помощью 'Enumerable. * 'методы. Вы должны '.Compile()' их или использовать методы 'IQueryable <> и' Queryable. * '. – xanatos

ответ

1

Успокойся людей, после некоторых исследований я обнаружил, что не хватает в моем коде ...

В случае кулака:

Expression.Call(
    typeof(Enumerable), 
    "Select", 
    new Type[] 
    { 
     typeof(SomeClass), 
     typeof(string) 
    }, 
    Expression.Constant(someList), // <---------------- HERE IT IS 
    Expression.Lambda(
     someProperty, 
     someParam 
    ) 
); 

ко второму случаю, я создал " новый "через код:

var bind = Expression.Bind(typeof(OtherClass).GetProperty("SomeProperty"), someProperty); 
var otherClassNew = Expression.New(typeof(OtherClass)); 
var otherClassInit = Expression.MemberInit(otherClassNew, bind); 

В любом случае, спасибо всем за помощь!

-1

Некоторые примеры того, что вы могли бы сделать:

Учитывая

public class SomeClass 
{ 
    public string SomeProperty { get; set; } 
} 

и

var someList = new List<SomeClass>(); 
someList.Add(new SomeClass { SomeProperty = "Hello" }); 

var someParam = Expression.Parameter(typeof(SomeClass), "p"); 
var someProperty = Expression.Property(someParam, "SomeProperty"); 

Expression<Func<SomeClass, string>> lambda = Expression.Lambda<Func<SomeClass, string>>(someProperty, someParam); // p => p.SomeProperty 

Использование IEnumerable<SomeClass> ... Обратите внимание на .Compile()

Func<SomeClass, string> compiled = lambda.Compile(); 
IEnumerable<string> q1 = someList.Select(compiled); 

Вы не должны использовать AsQueryable(), но в модульных тестах и ​​программах экспериментов (например, этот). Просто, чтобы сделать @Peter счастливым, я добавлю еще одно возможное условие: если вы действительно знаете, что он делает (не думаю, вы знаете, что он делает, действительно!), То вы можете использовать его. Но если это первый раз, когда вы его используете, я все же предлагаю вам спросить, если вы правы в использовании.

IQueryable<SomeClass> queryable = someList.AsQueryable(); 

Непосредственно с помощью Queryable.Select()

IQueryable<string> q2 = queryable.Select(lambda); 

Построение Select и с помощью CreateQuery (это очень похоже на то, что внутренне Queryable.Select делает), чтобы «впрыснуть» это в запросе.

MethodInfo select = (from x in typeof(Queryable).GetMethods() 
        where x.Name == "Select" && x.IsGenericMethod 
        let gens = x.GetGenericArguments() 
        where gens.Length == 2 
        let pars = x.GetParameters() 
        where pars.Length == 2 && 
         pars[0].ParameterType == typeof(IQueryable<>).MakeGenericType(gens[0]) && 
         pars[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(gens)) 
        select x).Single().MakeGenericMethod(typeof(SomeClass), typeof(string)); 

MethodCallExpression select2 = Expression.Call(null, select, Expression.Constant(queryable), lambda); 

IQueryProvider provider = queryable.Provider; 
IQueryable<string> q3 = provider.CreateQuery<string>(select2); 
+0

Почему мы должны «никогда не использовать AsQueryable(), а в модульных тестах и ​​экспериментах»? Учитывая такое сильное заявление, было бы хорошо, если бы вы поддержали его с помощью короткого объяснения. –

+0

@PeterLillevold. Потому что он не делает то, что думают 90% программистов на C#. Если кто-то использует его за пределами единичного теста, 9 раз из 10 он не знает, что делает, и просто бросает вещи, пока не скомпилируется. – xanatos

+0

@PeterLillevold. Правильное предложение должно быть «вы не должны использовать его, если не знаете, что делаете», но мало кто признает, что они бросают вещи, пока не скомпилируются, поэтому проще использовать абсолюты ... Например, «don 't использовать 'float' и' double', если вы не программист или не создаете PDF-файл. – xanatos

Смежные вопросы