Динамические запросы Linq для нескольких фильтров времени выполнения - PullRequest
1 голос
/ 08 июля 2011

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

Для этого сценария рассмотрим сущность, связанную с видом сетки, как эту:

public class Person
{
    public string name;
    public string sex;
    public int age;
}

Я бы хотел дать пользователю возможность фильтровать результаты в сетке, используя серию фильтров, по которым он мог бы выбрать столбец, который он хочет фильтровать (возраст, пол, имя), оператор (больше, чем , равно, содержит и т. д.) и значение.

Класс фильтра будет выглядеть примерно так:

public class filter
{
    public string column;
    public string operator;
    public string value;
}

Мой вопрос: как я могу применить эти фильтры к данным, отображаемым в сетке, используя динамически создаваемые запросы linq?

Ответы [ 2 ]

3 голосов
/ 08 июля 2011

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

Вот пример реализации:

void Main()
{
    var fieldName = "LastName";
    var value = "test";

    var db = new List<Person>() {
        new Person() { name = "fred jones", sex = "male", age = 55 },
        new Person() { name = "samantha jones", sex = "female", age = 45 },
        new Person() { name = "cindy jones", sex = "female", age = 6 }
    };

    // single query
    db.Where( Person.GetFilter("sex", "==", "female").Compile() ).Dump();

    // OR example
    db.Where( 
        PredicateBuilder.Or<Person>(
            Person.GetFilter("sex", "==", "male"), 
            Person.GetFilter("age", "<", 50)
        ).Compile()
    ).Dump();

}
class Person
{
    public string name;
    public string sex;
    public int age;

    public static Expression<Func<Person,bool>> GetFilter<T>(string column, string @operator, T value)
    {
        var ops = new Dictionary<string, Func<Expression, Expression, Expression>>() {
            { "==", (x,y) => Expression.Equal(x,y) },
            { "<=", (x,y) => Expression.LessThanOrEqual(x,y) },
            { ">=", (x,y) => Expression.GreaterThanOrEqual(x,y) },
            { ">", (x,y) => Expression.GreaterThan(x,y) },
            { "<", (x,y) => Expression.LessThan(x,y) },
        };

        var param = Expression.Parameter(typeof(Person));
        var deref = Expression.PropertyOrField(param, column);
        var testval = Expression.Constant(value);

        return Expression.Lambda<Func<Person,bool>>(
            ops[@operator](deref, testval),
            param);
    }
}

Примечание. Я использовал Linqpad для проверки этого, поэтому замените вызов .Dump ()с тем, что подходит в вашем контексте для отображения данных.Для построения операторов OR или AND требуется класс PredicateBuilder .Кроме того, это будет неуспешно, если в качестве параметра-значения будет передан неправильный тип (например, строка, передаваемая при сравнении age, выдает «InvalidOperationException: двоичный оператор Equal не определен для типов« System.String »и« System.Int32 »).. ").

Если кто-нибудь может сказать мне лучший способ справиться с превращением строкового представления оператора в класс Expression, дайте мне знать, поскольку я не очень доволен вышеуказанным способом, но этобыл самым быстрым, о чем я мог думать в то время.

2 голосов
/ 08 июля 2011

Вы можете попробовать использовать библиотеку Dynamic LINQ:

http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Или вы можете условно добавить фильтры к своим запросам:

var dc = new MyDataContext(); // wrap with using block in production
var query = dc.MyTable.AsQueryable();

if(filter1)
  query = query.Where(i=>i.Name.Contains(text));

if(filter2)
  query = query.Where(i=>i.Age == age);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...