开发者

Contains functionality in Linq to XML Where Clause

I'm having unexpected behavior with the .Contains() function of the where clause in Linq to XML. It seems to be functioning like "==" not Contains() in the string function.

Example:

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 is a list of strings. Let's say it contains 3 values:

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

What happens now, is that the Query works perfectly if

<itemtype>videos</itemtype> 

is the XML node.

However, if the node is

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

the IEnumerable returns blank, which to me says the operand is functioning like "==" not "C开发者_如何学Contains()".

Any idea what I'm doing wrong?

Winning answer from dtb:

replace

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

with

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


Try this:

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

This way you're checking that the element's value contains any of the strings in _filters. The use of the null coalescing operator ensures a NullReferenceException isn't thrown when the itemtype node doesn't exist since it is replaced with an empty string.

The other approach is to use let and filter out the nulls:

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")
        }

Note that String.Contains is case sensitive. So a check for "videos" won't match on "Videos" with a capital "V". To ignore case you can use String.IndexOf in this manner:

_filters.Any(filter => itemtype.IndexOf(filter, StringComparison.InvariantCultureIgnoreCase) >= 0)


You are checking if the array _filters has an element with the value "videos mission critial document advertising" (which is not the case), rather than if "videos mission critial document advertising" contains any of the elements in _filters.

Try this:

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


The problem is that you are making false assumptions about the way the Contains method works. (Also see the String.Contains() documentation The contains method returns true if "a sequence contains a specific element". In the example you described, both the _filters array and the itemtype text contains the string videos, but neither contain each other. A more appropriate test would be to use the following extension method:

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;
    }
}

So, your correct LINQ query would be:

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
        };

It may also be helpful to review this post: How do I use LINQ Contains(string[]) instead of Contains(string), which is similar but specifically targets the String.Contains method instead of the Enumerable.Contains extension.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜