Фильтрация по подзапросам с использованием Dynamic LINQ - PullRequest
5 голосов
/ 17 ноября 2011

Я работаю над большим приложением, которое использует Динамическую библиотеку LINQ для фильтрации.

Все классы в моем уровне доступа к данным являются производными от общего предка DALAncestor, который, помимо прочего, определяет метод GetData: public abstract List GetData (Filter filter)

Реализация этого метода для клиента будет выглядеть (упрощенно):

public override List<Entities.Customer> GetData(Filter filter) {
  var customers = from c in db.Customers
                  select new Entites.Customer { 
                    ID = c.ID,
                    FullName = c.Name,
                    Country = c.Country
                  };

   return = filter.Apply(customers).ToList();
}

«Применить» - это метод, который принимает набор условий и применяет все из них с помощью Dynamic LINQ Libary.

Метод GetData вызывается так:

private void DemoCallGetDataMethod() {
  var filter = new Filter();
  filter.AddCondition("Country", "Austria");

  var list = myCustomerDAL.GetData(filter);
  // do something
}

SQL Server получает выбранную оценку:

SELECT [t0].ID, [t0].Name, [t0].Country
  FROM Customer [t0]
 WHERE [t0].Country = @p0

(@ p0 - параметр со значением, установленным в «Австрия»).

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

Я не могу найти решение для этого: «Получить всех клиентов из Австрии, которые купили Pepsi».

Я пробовал несколько вещей, но ни одна из них не работает. Основная идея:
1. Добавление поддержки CONTAINS в DynamicLibrary
2. Измените мой код на

public override List<Entities.Customer> GetData(Filter filter) {
  var customers = from c in db.Customers
                  select new Entites.Customer { 
                    ID = c.ID,
                    FullName = c.Name,
                    Country = c.Country,
                    Items = c.Order.SelectMany(o => o.Item).Select(i => i.ItemId).ToList()
                  };

  return = filter.Apply(customers).ToList();
}

private void DemoCallGetDataMethod() {
  var filter = new Filter();
  filter.AddCondition("Country","=", "Austria");
  filter.AddCondition("Items","Contains", "11"); // 11 = Id for Pepsi

  var list = myCustomerDAL.GetData(filter);

  // do something
}

Исключение составляет:

System.InvalidOperationException: универсальный метод «Contains» для типа «System.Linq.Enumerable» не совместим с предоставленными аргументами и аргументами типа. Аргументы типа не должны предоставляться, если метод не является универсальным.

Кто-нибудь знает, что я делаю не так? Или я просто иду в неправильном направлении, и я должен попробовать другой подход? Который? :)

РЕДАКТИРОВАНИЕ: изменил мой пример

Ответы [ 2 ]

2 голосов
/ 17 ноября 2011

Я использую PredicateBuilder для динамических запросов в моих приложениях.

Predicate Builder

В вашем случае я представляю, что это будет что-то вроде:

 string MyCountry = "Austria";
 string MyProductId = 11

 var Predicate = PredicateBuilder.True<db.Customers>();
 Predicate = Predicate.And(p=>p.Country ==MyCountry && p.Order.SelectMany(o=>o.Item).Where(i=>i.id == 11).Count() >0);

 var list = Customers.Where(Predicate).Select(s=>s).ToList();
2 голосов
/ 17 ноября 2011

@ user182630 Я хочу иметь возможность фильтрации по значениям из подвыборов в целом, а не только для конкретного случая.Подход из этой статьи работает, только если вы жестко программируете и фильтруете, что я не могу сделать.- Марко Юванчич 9 минут назад

Прочитайте эту статью http://csharpindepth.com/articles/chapter5/closures.aspx

...