Динамическое выражение Linq для IEnumerable‹int›.contains(MemberExpression)

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

List<int> ids = new List<int>();

// Fill ids with data

db.Where(a => ids.Contains(a.Id));

Вот куда я попал, но мне все еще чего-то не хватает:

MemberExpression me = Expression.Property(pe, typeof(T).GetProperty(property));

Expression callContains = Expression.Call(typeof(System.Linq.Enumerable), "Contains", new Type[] { me.Type }, me);

Как мне правильно делать то, что я хочу?


person TheNerd    schedule 28.08.2013    source источник
comment
У меня только один вопрос. Почему вы хотите сделать что-то подобное?   -  person Ondrej Janacek    schedule 28.08.2013
comment
Я создаю очень большой и очень сложный механизм поиска, используя linq. Имеющаяся у меня структура не разрешает доступ к объектам IQueryable, а только к нашим пользовательским моделям, поэтому я не могу просто связать запросы, как обычно. Вместо этого я динамически строю выражение по частям в зависимости от выбранных параметров поиска.   -  person TheNerd    schedule 28.08.2013


Ответы (3)


Поскольку Contains — это метод расширения, вам также придется указать коллекцию ids в качестве параметра, в данном случае — как ConstantExpression.

Ваша реализация может немного отличаться, но она будет выглядеть примерно так:

public static IQueryable<T> DynamicContains<T, TProperty>(
    this IQueryable<T> query, 
    string property, 
    IEnumerable<TProperty> items)
{
    var pe = Expression.Parameter(typeof(T));
    var me = Expression.Property(pe, property);
    var ce = Expression.Constant(items); 
    var call = Expression.Call(typeof(Enumerable), "Contains", new[] { me.Type }, ce, me);
    var lambda = Expression.Lambda<Func<T, bool>>(call, pe);
    return query.Where(lambda);
}

db.DynamicContains("Id", ids);
person p.s.w.g    schedule 28.08.2013
comment
Я не вижу аргумента property где-либо в вашем методе, я думаю, вы имеете в виду typeof(T).GetProperty(property) (вместо Id). - person King King; 28.08.2013
comment
Спасибо! Это именно то, что мне было нужно, и я вижу, чего мне не хватало раньше. - person TheNerd; 28.08.2013
comment
Почему бы вам не использовать эту перегрузку Expression.Property, чтобы сделать второй строка только var me = Expression.Property(pe, property);? - person Scott Chamberlain; 28.08.2013

Вы можете добавить ссылку на dll Mono.CSharp, затем вы можете использовать класс Evaluator для компиляции кодов csharp на лету, затем объединить строки для легкого создания запросов linq, а затем скомпилировать их, это не медленно, и это легко

person Yaser Moradi    schedule 28.08.2013

В дополнение к ответу @p-s-w-g DynamicNotContains:

    public static IQueryable<T> DynamicNotContains<T, TProperty>(this IQueryable<T> query, string property, IEnumerable<TProperty> items)
    {        
        var pe = Expression.Parameter(typeof(T));
        var me = Expression.Property(pe, property);
        var ce = Expression.Constant(items);
        var call = Expression.Call(typeof(Enumerable), "Contains", new[] { me.Type }, ce, me);
        var lambda = Expression.Lambda<Func<T, bool>>(Expression.Not(call), pe);

        return query.Where(lambda);
    }
db.DynamicNotContains("Id", ids);
person Mahmoud Moravej    schedule 06.06.2017