Преобразование Где из предложения LINQ в динамический LINQ - PullRequest
3 голосов
/ 21 марта 2012

Я хочу перейти с

        var selectData = (from i in data
                          where i.Name == "Bob1"
                          select i);

на

        var selectData = (from i in data
                          select i).Where("Name==Bob1");

Я пробовал разные подходы (AsQueryable, Where<SomeData>), но не могу получить второеФорма для компиляции.

Я плохо разбираюсь в общих методах расширения C #.<Tsource> не имеет смысла для меня, так что это может быть проблемой.Кроме того, я не понимаю, почему я могу набрать .Where(), когда intellisense отображает только .Where<> (универсальный).Я ожидаю увидеть вторую Where без общего символа ... увы нет.

Класс

public class SomeData
{
    public string Name { get; set; }
    public string Address { get; set; }
}

ОБНОВЛЕНИЕ Кажется, есть некоторая путаница относительно того, как можно использовать Where (), что вполне может быть моей ошибкой.Пожалуйста, смотрите связанный вопрос.Исходя из этого ответа, имя свойства в предложении where является совершенно законным.Мне нужно, чтобы свойство оставалось строкой.Если это означает, что требуется динамический LINQ, пусть будет так ... вот что мне нужно.

Ответы [ 5 ]

2 голосов
/ 21 марта 2012

Со всей вашей помощью мне удалось получить преобразование в функцию.

  1. Установить Dynamic LINQ (я использовал NUGET. Поиск в Интернете для System.Linq.Dynamic)
  2. Добавить using System.Linq.Dynamic
  3. Запрос должен иметь форму

        var selectData = (from i in data
                          select i).AsQueryable().Where("Name = @0","Bob1");//@0 is called an identifier.  "Name = Bob1" straight up fails.
    
  4. Установить библиотеку образцов C # ScottGU ... это помогает. ( VB ) ( Исходное сообщение )

2 голосов
/ 21 марта 2012
var selectData = (from i in data
                  select i).Where(datum => datum.Name == "Bob1");

Метод Where принимает делегат, а не строку, поэтому вам нужно передать делегат или лямбду.

Редактировать: основываясь на вашем комментарии к одному из других ответов, вам нужно будет использовать Reflection, чтобы сделать поиск значения свойства динамическим.

Редактировать: похоже, вам нужно загрузить исходный код для библиотеки Dynamic Linq отдельно.

1 голос
/ 21 марта 2012

UPDATE

Сначала я неправильно понял вопрос; Решение проблемы - скачать Dynamic Linq и ссылаться на него. Ниже я оставлю свой ответ, в котором рассматриваются побочные вопросы, которые вы задавали о типовых методах расширения.


var selectData = (from i in data 
    select i).Where(d => d.Name=="Bob1");

Но почему бы не это:

var selectData = data.Where(d => d.Name=="Bob1");

Что касается "неуниверсальной" версии where, такой вещи не существует. В приведенных выше вызовах параметр типа универсального метода является неявным; это было выведено компилятором, который компилирует вызов точно так, как он скомпилирует это:

var selectData = data.Where<SomeData>(d => d.Name=="Bob1");

Возможно, эскизная реализация метода Where поможет уменьшить путаницу с параметром TSource:

public static IEnumerable<TSource> Where(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    foreach (TSource item in source)
        if (predicate(item))
            yield return item;
}

TSource - это тип элемента последовательности, которую вы запрашиваете. Это также тип элемента последовательности результатов.

Компилятору необходимо знать тип по крайней мере по двум причинам:

Во-первых, нам нужно вызвать функцию для каждого элемента, чтобы определить, включать ли его в последовательность результатов. Компилятор должен знать, что референт параметра predicate может безопасно принимать параметр типа TSource.

Вторая причина в этом случае несколько тривиальна; item должно быть совместимо с присваиванием с TSource, потому что оно используется в операторе yield return. Конечно, оно совместимо, потому что оно того же типа.

0 голосов
/ 21 марта 2012

Вот простой код, использующий дерево выражений, чтобы делать то, что вы хотите.Это будет работать только для запросов Property == "..." к этому конкретному типу.Вы, конечно, можете изменить это и сделать его настолько общим, насколько вам нужно.

    public void Test()
    {
        List<SomeData> data = new List<SomeData>();
        data.Add(new SomeData("Mark", "Ledgewood Drive"));
        data.Add(new SomeData("Tim", "Sumpter Drive"));
        data.Add(new SomeData("Sean", "Leroy Drive"));
        data.Add(new SomeData("Bob", "Wilmington Road"));
        data.Add(new SomeData("Sean", "Sunset Blvd"));

        List<SomeData> result = data.Where(BuildExpression("Name", "Mark")).ToList();
        List<SomeData> result2 = data.Where(BuildExpression("Address", "Wilmington Road")).ToList();
    }

    private Func<SomeData, bool> BuildExpression(string propertyName, string value)
    {
        ParameterExpression pe = Expression.Parameter(typeof(SomeData), "someData");
        Expression left = Expression.Property(pe, propertyName);
        Expression right = Expression.Constant(value);
        BinaryExpression binary = Expression.Equal(left, right);
        Expression<Func<SomeData, bool>> lambda = Expression.Lambda<Func<SomeData, bool>>(binary, pe);
        return lambda.Compile();
    }
0 голосов
/ 21 марта 2012

Я считаю, что это то, что вы ищете:

http://www.albahari.com/nutshell/predicatebuilder.aspx

Пример

IQueryable<Product> SearchProducts (params string[] keywords)
{
  var predicate = PredicateBuilder.False<Product>();

  foreach (string keyword in keywords)
  {
    string temp = keyword;
    predicate = predicate.Or (p => p.Description.Contains (temp));
  }
  return dataContext.Products.Where (predicate);
}

Исходный код

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;

public static class PredicateBuilder
{
  public static Expression<Func<T, bool>> True<T> ()  { return f => true;  }
  public static Expression<Func<T, bool>> False<T> () { return f => false; }

  public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
                                                      Expression<Func<T, bool>> expr2)
  {
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
          (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
  }

  public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
                                                       Expression<Func<T, bool>> expr2)
  {
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
          (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
  }
}
...