Сужение набора результатов с помощью LINQ - PullRequest
1 голос
/ 13 мая 2011

Скажем, у меня есть метод с именем GetCatsByColor, который принимает цвет как строку, метод GetCatsByName, который принимает имя как строку, и GetCatsByBirthDate, который принимает два DateTime с, действуя в качестве диапазона времени.

Теперь скажите, что у меня есть класс CatFilter, который содержит List имен, List цветов и два DateTime s, представляющих дату "от" и дату "до"времени.Я пытаюсь создать метод GetFilteredCats, который принимает один из этих Filter объектов и возвращает коллекцию кошек, которые соответствуют спецификациям данного Filter.

У менятрудно найти эффективный способ получения желаемых результатов, в идеале используя LINQ / лямбда-выражения.

Каков наилучший способ объединения?Какие методы расширения я должен смотреть?Модификация коллекции в цикле foreach обычно не рекомендуется / не возможна, так какой должна быть моя стратегия?

Ответы [ 4 ]

1 голос
/ 13 мая 2011

Что я обычно делаю, это помещаю галочку в предложении where, которая проверяет, нужен ли фильтр, перед тем, как выполнять настоящий фильтр.Когда среде выполнения необходимо оценить фильтр, он полностью пропускается, если он не нужен.

public class CatFilter
{
    public List<string> Names = new List<string>();
    public List<string> Colors = new List<string>();
    public DateTime? BirthDateStartRange = null;
    public DateTime? BirthDateEndRange = null;
}

public List<Cat> GetFilteredCats(CatFilter filter)
{
    List<Cat> result = new List<Cat>();

    var query = cats
        .Where(a => !filter.Names.Any() || filter.Names.Contains(a.Name))
        .Where(a => !filter.Colors.Any() || filter.Colors.Contains(a.Color))
        .Where(a => filter.BirthDateStartRange == null || a.DateOfBirth >= filter.BirthDateStartRange)
        .Where(a => filter.BirthDateEndRange == null || a.DateOfBirth <= filter.BirthDateEndRange);

    result.AddRange(query);
    return result;
}

И затем вызывается так:

cats.Add(new Cat("Felix", "Black", DateTime.Today.AddDays(-1)));
cats.Add(new Cat("Garfield", "Orange", DateTime.Today.AddDays(-10)));

CatFilter filter = new CatFilter();
filter.Names.Add("Garfield");

List<Cat> result = GetFilteredCats(filter);
0 голосов
/ 13 мая 2011

Что-то вроде этого должно работать, пожалуйста, обратите внимание, что оно не проверено

List<string> names = new List<string>();
            List<Color> colors = new List<Color>();
            List<DateTime> dobs = new List<DateTime>();

            List<cat> cats = new List<cat>();


            var filtered = from c in cats
                           join n in names on c.name equals n
                           join cl in colors on c.color equals cl
                           join db in dobs on c.dob equals db

                           select c;

У вас также может быть какой-то список с двумя датами, в этом случае вам нужно будет поставить условие where, где c.dob<= date1 && c.dob> = date2 или что-то подобное.Надеюсь, это поможет.

0 голосов
/ 13 мая 2011

Вы можете использовать деревья выражений. Когда объект CatFilter передается в ваш метод GetFilteredCats на основе свойств, которые установлены для этого объекта, вы генерируете выражения (то есть видно из псевдокода ниже), которые вы объединяете и используете для создания полного запроса LINQ.

Что-то вроде:

Expression catFilter =
from cat in Cats
    where <Expression> and <Expression> and ...
select cat

Затем просто скомпилируйте (Expression.Compile) и выполните.

0 голосов
/ 13 мая 2011

Правильный способ сделать это состоит в том, чтобы сделать метод GetFilteredCats, принять ваш фильтр и вернуть правильный кот через состав LINQ:

IEnumerable<Cat> cats = //.. get all cats here

if (filter.FilterByColor)
   cats = cats.Where(c=>c.Color = filter.Color);

if (filter.FilterByName)
   cats = cats.Where(c=>c.Name = filter.Name);

if (filter.FilterByDate)
   cats = cats.Where(c=>c.Date > filter.FromDate && c.Date < filter.ToDate)

return cats.ToList(); // finally filter data and return them.

В случае исполнения. Я не думаю, что это может быть логичным, используя другой подход. Но это станет проблемой, когда вы начнете поражать миллионы кошек. И на этом этапе база данных должна использоваться вместо. Для вашего удобства в них есть разумная индексация и кластеризация.

...