Динамический LINQ на коллекцию? - PullRequest
5 голосов
/ 20 августа 2009

У меня есть проект, который просит меня сделать такую ​​БОЛЬШУЮ поисковую систему, но которая динамична. Я имею в виду, что у меня может быть от 0 до 9 основных «групп», которые имеют внутри что-то вроде бесконечной возможности «где» с «ИЛИ» или «И». Первым делом мы решили использовать Dynamic Linq, который является хорошей альтернативой для построения динамического запроса. Все это с помощью EF с домашней оберткой.

Проблема : Я не могу получить доступ к «Коллекции». Я имею в виду, что я могу легко получить доступ к указанному объекту (например, Customer.State.StateName = "New-York" ИЛИ Custoemr.State.StateName = "Quebec" ), но не могу найти способ доступ к чему-то вроде: "Customer.Orders.OrderID = 2 ИЛИ Customer.Orders.OrderID = 3" . Я могу легко понять это, потому что это коллекция, но как я могу это сделать?

Пожалуйста, помогите мне !!

** Извините за мой английский !!


Обновление

Я не достаточно ясно, я думаю, извините, потому что я французский ...

Моя проблема в том, что ничего не статично. Это поисковая система кандидатов для рекрутирующей компании, которая помещает кандидатов в предприятие. На странице, где менеджер может искать кандидата, он может «анализировать» по: домену (ам), городу (городам) или многим другим, которые пользователь заполнил при регистрации. Все это в формате (если бы это было в SQL):

[...] ГДЕ (domaine.domainID = 3 ИЛИ domaine.domainID = 5 ИЛИ domaine.domainID = 23) И (towns.cityID = 4, towns.city = 32) [...]

Так что я не могу сделать это с обычным форматом LINQ, например:

Candidat.Domaines.Where(domain => domain.DomainID == 3 || domain.DomainID == 5 || domain.DomainID == 23);

Даже оператор в паретезах является динамическим («И» или «ИЛИ»)! Вот почему мы пытаемся использовать Dynamic Linq, потому что он гораздо более гибкий.

Надеюсь, мне легче понять мою проблему ...


Обновление 2 Вот мой метод

private string BuildDomainsWhereClause() {
        StringBuilder theWhere = new StringBuilder();

        if (this.Domaines.NumberOfIDs > 0) {
            theWhere.Append("( ");

            theWhere.Append(string.Format("Domaines.Where( "));
            foreach (int i in this.Domaines.ListOfIDs) {
                if (this.Domaines.ListOfIDs.IndexOf(i) > 0) {
                    theWhere.Append(string.Format(" {0} ", this.DispoJours.AndOr == AndOrEnum.And ? "&&" : "||"));
                }
                theWhere.Append(string.Format("DomaineId == {0}", i));
            }
            theWhere.Append(" ))");
        }

        return theWhere.ToString();
    }

Он отлично работает, вместо этого он не возвращает логическое значение. Так как я должен? Ошибка: «Ожидается выражение типа« логическое »».

В конце он возвращает что-то вроде: «(Domaines.Where (DomaineId == 2 && DomaineId == 3 && DomaineId == 4 && DomaineId == 5))." который добавлен в мой запрос LINQ:

var queryWithWhere = from c in m_context.Candidats.Where(WHERE)
                                     select c;

Не забывайте, что есть еще 7 или 8 "возможных" добавленных вещей для поиска в ... Есть идеи?

Ответы [ 5 ]

5 голосов
/ 20 августа 2009

Что вам нужно сделать здесь, это построить LambdaExpression (точнее, Expression<Func<T, bool>>). Вы не можете использовать строку. Вы можете создать простое выражение, как это:

ParameterExpression p = Expression.Parameter(typeof(Domaine), "domaine");
Expression<Func<Domaine, bool>> wherePredicate = 
  Expression.Lambda<Func<Domaine, bool>>(
    Expression.Or(
      Expression.Equal(
        Expression.Property(p, "DomainID"),
        Expression.Constant(10)),
      Expression.Equal(
        Expression.Property(p, "DomainID"),
        Expression.Constant(11))
      ), p);

т.е.,

domaine.DomainID = 10 || domaine.DomainID = 11

Не очень читабельно, если вам нужно сделать это вручную.

Существует пример полностью работающего синтаксического анализатора выражений, который фактически сделает это за вас на основе строки в C # Samples для Visual Studio 2008 в MSDN Code Gallery, в разделе DynamicQuery . (Элемент управления LinqDataSource использует слегка измененную версию этого образца для внутреннего использования.)

4 голосов
/ 20 августа 2009

Наконец-то я понял, как хочу.

private string BuildDomainsWhereClause() {
        StringBuilder theWhere = new StringBuilder();

        if (this.Domains.NumberOfIDs > 0) {
            theWhere.Append("( ");

            foreach (int i in this.Domains.ListOfIDs) {
                if (this.Domains.ListOfIDs.IndexOf(i) > 0) {
                    theWhere.Append(string.Format(" {0} ", this.Domains.AndOr == AndOrEnum.And ? "&&" : "||"));
                }
                theWhere.Append(string.Format("Domains.Any(IdDomaine== {0})", i));
            }
            theWhere.Append(" )");
        }

        return theWhere.ToString();
    }

, которые выдают что-то вроде: "(DispoJours.Any (IdDispo == 3) && DispoJours.Any (IdDispo == 5))".

Все мои другие «Где строитель» будут делать то же самое с «&&», между которыми будет получен правильный результат.

и позже:

var queryWithWhere = from c in m_context.Candidats.Where(WHERE)
                     select c;

WHOOOHOOO !! Спасибо, ребята. Были очень полезны! Люблю этот сайт!


Обновление

Не забывайте, что я использую Dynamic Linq в этом запросе. Это не обычный запрос LINQ.

1 голос
/ 20 августа 2009

Правильно ли я понимаю, что оба Заказчика являются коллекцией, а Заказы - коллекцией, а Государство (очевидно) является Собственностью?

var q = from a in Customer
    from b in a.Orders
    where b.ID == 2
              || b.ID == 3
    select b;

Будет работать, я думаю.

Редактировать

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

public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values);

Из класса DynamicQueryable.

this.CountrySitesObject.Sites.AsQueryable().Where(w.WhereQuery, w.WhereParameters) 

(скопировано из моего кода).

1 голос
/ 20 августа 2009

Предполагая, что Customer.Orders возвращает коллекцию, именно поэтому вы не можете просто вызвать ее свойство.

Чтобы использовать LINQ для получения искомого ордера, вам нужно знать OrderID (или другое свойство), в этом случае вы можете сделать:

Customer.Orders.Find(order => order.OrderID == 2);

Редактировать: добавить выражение для поиска идентификатора 2 или 3 следующим образом:

Customer.Orders.FindAll(order => order.OrderID == 2 || order.OrderID == 3);
0 голосов
/ 20 августа 2009

Если вы отступите назад и спросите, что хочет сделать клиент.

Filter bug information.

Почему бы не экспортировать данные в Excel или указать Excel на таблицу SQL. Построить это не так весело, но это будет сделано за пару часов, а не дней или недель. :)

...