Перечислимый выбор по дереву выражений

Я изучаю "Дерево выражений", но мне не удается выполнить эти выражения:

// 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: ни один универсальный метод Select для типа System.Linq.Enumerable не совместим с предоставленными аргументами и аргументами типа. Не следует указывать аргументы типа, если метод не является универсальным.

Что касается «второго случая», я понятия не имею, как поступить.

Может ли кто-нибудь направить меня сюда?


person Alexandre Perez    schedule 26.06.2015    source источник
comment
Я думаю, что вам не хватает параметра IEnumerable SomeClass›, который будет между new Type[] и Expression.Lambda в вашем Expression.Call   -  person Will    schedule 26.06.2015
comment
Вы не можете напрямую использовать деревья выражений с методами Enumerable.*. Вы должны .Compile() их или использовать методы IQueryable<> и Queryable.*.   -  person xanatos    schedule 26.06.2015


Ответы (2)


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

Данный

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 счастливым, я добавлю еще одно возможное условие: если вы действительно знаете, что он делает (не думает, что знаете, что он делает, действительно< /strong>!), то вы можете его использовать. Но если вы используете его впервые, я все же предлагаю вам спросить на SO, правильно ли вы его используете.

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);
person xanatos    schedule 26.06.2015
comment
Почему мы никогда не должны использовать AsQueryable(), кроме как в модульных тестах и ​​экспериментальных программах? Учитывая такое сильное утверждение, было бы хорошо, если бы вы подкрепили его кратким пояснением. - person Peter Lillevold; 26.06.2015
comment
@PeterLillevold Потому что он не делает того, что думают 90% программистов на C #. Если кто-то использует его вне модульного теста, в 9 случаях из 10 он не знает, что делает, и просто бросает вещи, пока он не скомпилируется. - person xanatos; 26.06.2015
comment
@PeterLillevold Правильное предложение должно заключаться в том, что вы не должны использовать его, если не знаете, что делаете, но немногие признают, что они бросают вещи до тех пор, пока они не скомпилируются, поэтому проще использовать абсолюты ... Например, не используйте float и double, если вы не программист игр или не создаете PDF. - person xanatos; 26.06.2015
comment
хорошо, поэтому я думаю, что ваше утверждение весьма самоуверенно и не подчеркивает ничего, что имеет отношение к вопросу ОП. Использование абсолютов не повысит осведомленность и понимание, если вы не подкрепите их здравыми рассуждениями. - person Peter Lillevold; 26.06.2015
comment
@PeterLillevold Вопрос был не о AsQueryable(), и я хотел дать понять, что я не одобряю его использование где-либо, кроме как в каком-то месте. Теперь... Вероятно, есть случаи использования, когда вы можете использовать AsQueryable(), но вы должны быть такого высокого роста, чтобы их можно было распознать. - person xanatos; 26.06.2015
comment
опять же, подкрепите это почему. С технической точки зрения, почему существует риск, связанный с использованием AsQueryable()? Я понимаю, что вы считаете, что 90% программистов на C# просто обезьяны, бросающие код в компилятор, вы имеете право на это мнение. Но это мнение вряд ли имеет отношение к данному вопросу. - person Peter Lillevold; 26.06.2015
comment
@PeterLillevold Нет никакого риска. риск — это когда вы уважаете потенциально нулевой указатель. Это больше похоже на то, что если вы хотите его использовать, прочитайте, что он делает, мы не говорим об этом... Я уверен, что на SO есть вопросы о том, что он делает. Этот вопрос не о AsQueryable(), но только потому, что я его использовал, кто-то скопирует и вставит мой код в свой код и будет использовать AsQueryable(). Я использовал плохой шаблон, и я прямо сказал, что это плохой шаблон, и что это нормально только потому, что я использую его, чтобы показать что-то (использование IQueryable<>), а не использовать его в производственном коде чтения. - person xanatos; 26.06.2015
comment
все еще не доволен :) вы не объяснили массам бедных разработчиков C #, что AsQueryable() делает, что такое зло. Либо дайте хорошее (краткое) объяснение того, что он делает, с чем следует быть осторожным, или полностью удалите аргумент. - person Peter Lillevold; 26.06.2015
comment
@PeterLillevold Мне не нужно / должен объяснять каждое мое мнение в каждом месте, где я его использую. Если вам не нравится мое мнение, вы можете его опровергнуть, а можете проигнорировать. Например, вместо того, чтобы писать 4 комментария, вы могли бы просто написать AsQueryable() полезно, если вы хотите открыть небольшой кеш, как будто это настоящая БД, чтобы вы могли переключать их на лету. .. На что я бы ответил да, верно, видел раз в 5 лет я видел AsQueryable()... В других случаях это было somequeryable.Where(somedelegatethatisntanexpression).AsQueryable() - person xanatos; 26.06.2015
comment
Большое вам спасибо за это, 5 лет спустя, и это только что спасло мой бекон! - person Andrew HB; 20.03.2020

Успокойтесь, ребята, после некоторых исследований я нашел то, чего не хватало в моем коде...

По первому делу:

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

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

person Alexandre Perez    schedule 29.06.2015