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.
精彩评论