Содержит функциональность в Linq to XML Where Clause - PullRequest
2 голосов
/ 07 июня 2011

У меня неожиданное поведение с функцией .Contains () предложения where в Linq to XML.Кажется, он работает как "==", а не Contains () в строковой функции.

Пример:

var q = from sr in SearchResults.Descendants("Result")
    where _filters.Contains((string)sr.Element("itemtype"))
    orderby (string)sr.Element("ipitemtype") ascending
    select new SearchItem
    {
        //Create Object
        ID = (string)sr.Element("blabla"),
    }

_filters - это список строк.Допустим, он содержит 3 значения:

_filters[0] = "videos";
_filters[1] = "documents";
_filters[2] = "cat pictures";

Что происходит сейчас, так это то, что запрос работает отлично, если

<itemtype>videos</itemtype> 

является узлом XML.

Однако, если узел является

<itemtype>videos mission critical document advertising</itemtype>, 

, IEnumerable возвращает пустое значение, которое мне говорит, что операнд работает как "==", а не "Contains ()".

Есть идеи, что я делаю не так?

Победный ответ от dtb:

заменить

where _filters.Contains((string)sr.Element("itemtype"))

на

where _filters.Any(filter => ((string)sr.Element("itemtype")).Contains(filter))

Ответы [ 3 ]

2 голосов
/ 07 июня 2011

Попробуйте это:

_filters.Any(s => ((string)sr.Element("itemtype") ?? "").Contains(s))

Таким образом, вы проверяете, что значение элемента содержит любую из строк в _filters.Использование оператора объединения нулей гарантирует, что NullReferenceException не будет выброшено, когда узел itemtype не существует, поскольку он заменен пустой строкой.

Другой подход заключается в использовании letи отфильтруйте нули:

var q = from sr in SearchResults.Descendants("Result")
        let itemtype = (string)sr.Element("itemtype")
        where itemtype != null &&
              _filters.Any(filter => itemtype.Contains(filter))
        orderby (string)sr.Element("ipitemtype") ascending
        select new SearchItem
        {
            //Create Object
            ID = (string)sr.Element("blabla")
        }

Обратите внимание, что String.Contains чувствителен к регистру.Поэтому проверка для «видео» не будет совпадать с «Видео» с большой буквы «V».Чтобы игнорировать регистр, вы можете использовать String.IndexOf таким образом:

_filters.Any(filter => itemtype.IndexOf(filter, StringComparison.InvariantCultureIgnoreCase) >= 0)
1 голос
/ 07 июня 2011

Проблема в том, что вы делаете ложные предположения о том, как работает метод Contains . (Также см. Документацию String.Contains() . Метод метода возвращает значение true, если «последовательность содержит определенный элемент». В описанном вами примере как массив _filters, так и текст itemtype содержат строку videos, но не содержат друг друга. Более подходящим тестом будет использование следующего метода расширения:

public static class ContainsAnyExtension
{
    public static bool ContainsAny<T>(this IEnumerable<T> enumerable, params T[] elements)
    {
        if(enumerable == null) throw new ArgumentNullException("enumerable");
        if(!enumerable.Any() || elements.Length == 0) return false;
        foreach(var element in elements){
           if(enumerable.Contains(element)){
               return true;
           }
        }
        return false;
    }
}

Итак, ваш правильный запрос LINQ будет:

var q = from sr in SearchResults.Descendants("Result")
        where ((string)sr.Element("itemtype")).ContainsAny(_filters)
        orderby ((string)sr.Element("ipitemtype")) ascending
        select new SearchItem
        {
            ID = sr.Element("blabla").Value
        };

Также может быть полезно просмотреть этот пост: Как использовать LINQ Contains (строка []) вместо Contains (строка) , который аналогичен, но специально нацелен на метод String.Contains вместо Enumerable.Contains расширение.

1 голос
/ 07 июня 2011

Вы проверяете, есть ли в массиве _filters элемент со значением "videos mission critial document advertising" (это не так), вместо того, чтобы "videos mission critial document advertising" содержал какой-либо из элементов в _filters.

* 1006.* Попробуйте это:
where _filters.Any(filter => ((string)sr.Element("itemtype")).Contains(filter))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...