Rendre NameValueCollection accessible à la requête LINQ
Comment rendre NameValueCollection
accessible à L'opérateur de requête LINQ tel que where, join, groupby?
J'ai essayé le ci-dessous:
private NameValueCollection RequestFields()
{
NameValueCollection nvc = new NameValueCollection()
{
{"emailOption: blah Blah", "true"},
{"emailOption: blah Blah2", "false"},
{"nothing", "false"},
{"nothinger", "true"}
};
return nvc;
}
public void GetSelectedEmail()
{
NameValueCollection nvc = RequestFields();
IQueryable queryable = nvc.AsQueryable();
}
Mais j'ai eu une ArgumentException me disant que la source n'est pas IEnumerable .
7 réponses
Vous devez "soulever" le IEnumerable
non générique à un IEnumerable<string>
. Il a été suggéré d'utiliser OfType
mais c'est une méthode de filtrage. Ce que vous faites est l'équivalent d'un cast, pour lequel il y a l'opérateur Cast
:
var fields = RequestFields().Cast<string>();
Comme Frans l'a souligné, cela ne donne accès qu'aux clés. Vous devrez toujours indexer dans la collection pour les valeurs. Voici une méthode d'extension pour extraire KeyValuePair
s du NameValueCollection
:
public static IEnumerable<KeyValuePair<string, string>> ToPairs(this NameValueCollection collection)
{
if(collection == null)
{
throw new ArgumentNullException("collection");
}
return collection.Cast<string>().Select(key => new KeyValuePair<string, string>(key, collection[key]));
}
Edit:, En réponse à @Ruben Demande de Bartelink, voici comment accéder à l'ensemble complet des valeurs pour chaque clé en utilisant ToLookup
:
public static ILookup<string, string> ToLookup(this NameValueCollection collection)
{
if(collection == null)
{
throw new ArgumentNullException("collection");
}
var pairs =
from key in collection.Cast<String>()
from value in collection.GetValues(key)
select new { key, value };
return pairs.ToLookup(pair => pair.key, pair => pair.value);
}
AsQueryable
doit prendre un IEnumerable<T>
, un générique. NameValueCollection
implémente IEnumerable
, ce qui est différent.
Au lieu de ceci:
{
NameValueCollection nvc = RequestFields();
IQueryable queryable = nvc.AsQueryable();
}
Essayez OfType (Il accepte l'interface non générique)
{
NameValueCollection nvc = RequestFields();
IEnumerable<string> canBeQueried = nvc.OfType<string>();
IEnumerable<string> query =
canBeQueried.Where(s => s.StartsWith("abc"));
}
Un dictionnaire est probablement plus proche de ce que vous voulez utiliser car il remplira plus de rôles que NameValueCollection remplit. C'est une variante de la solution de Bryan Watts:
public static class CollectionExtensions
{
public static IDictionary<string, string> ToDictionary(this NameValueCollection source)
{
return source.Cast<string>().Select(s => new { Key = s, Value = source[s] }).ToDictionary(p => p.Key, p => p.Value);
}
}
Je sais que je suis en retard à la fête mais je voulais juste ajouter ma réponse qui n'implique pas la méthode d'extension .Cast
mais utilise plutôt la propriété AllKeys:
var fields = RequestFields().AllKeys;
Cela permettrait la méthode d'extension suivante:
public static IEnumerable<KeyValuePair<string, string>> ToPairs(this NameValueCollection collection)
{
if(collection == null)
{
throw new ArgumentNullException("collection");
}
return collection.AllKeys.Select(key => new KeyValuePair<string, string>(key, collection[key]));
}
J'espère que cela aidera les futurs visiteurs
Le problème est que la collection implémente IEnumerable
(par opposition à IEnumerable<T>
) et l'énumération de la collection renvoie les clés, pas les paires.
Si j'étais vous, j'utiliserais un Dictionary<string, string>
qui est énumérable et peut être utilisé avec LINQ.
Pour moi, la variante ToLookup
de la réponse de @Bryan Watts (+1'D) représente de loin l'approche la plus claire pour l'utiliser en lecture seule.
Pour mon cas d'utilisation, je manipule une chaîne de requête à utiliser avec Linq2Rest et j'ai aussi besoin de tout retourner en NameValueCollection
à la fin, donc j'ai un ensemble de méthodes d'extension pour {[3] } qui offrent des opérations plus granulaires (pour fonctionner à la fois par nom de paramètre (AsEnumerable
) et par ToNameValueCollection
(de l'une ou l'autre représentation)).
Exemple de consommation:
public static NameValueCollection WithoutPagingOperators( this NameValueCollection that )
{
return that.AsEnumerable()
.Where( @param => @param.Key != OdataParameters.Skip
&& @param.Key != OdataParameters.Top )
.ToNameValueCollection();
}
Code:
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
public static class NamedValueCollectionExtensions
{
public static IEnumerable<KeyValuePair<string, string[]>> AsEnumerable( this NameValueCollection that )
{
return that
.Cast<string>() // doesn't implement IEnumerable<T>, but does implement IEnumerable
.Select( ( item, index ) => // enable indexing by integer rather than string
new KeyValuePair<string, string[]>( item, that.GetValues( index ) ) ); // if you use the indexer or GetValue it flattens multiple values for a key, Joining them with a ',' which we don't want
}
public static IEnumerable<KeyValuePair<string, string>> AsKeyValuePairs( this IEnumerable<KeyValuePair<string, string[]>> that )
{
return that
.SelectMany( item =>
item.Value.Select( value =>
new KeyValuePair<string, string>( item.Key, value ) ) );
}
public static NameValueCollection ToNameValueCollection( this IEnumerable<KeyValuePair<string, string[]>> that )
{
return that.AsKeyValuePairs().ToNameValueCollection();
}
public static NameValueCollection ToNameValueCollection( this IEnumerable<KeyValuePair<string, string>> that )
{
var result = new NameValueCollection();
foreach ( KeyValuePair<string, string> item in that )
result.Add( item.Key, item.Value );
return result;
}
}
Je ne vois pas vraiment pourquoi quelqu'un aurait besoin d'ajouter une méthode d'extension.
Voici quelques différentes façons de le faire dans VB.NET. il comprend 4 formes intermédiaires différentes de IEnumerable: Array, Tuple, Anonymous et KeyValuePair. Pour l'équivalent C# aller au convertisseur.telerik dot com et le convertir.
Dim nvc As New NameValueCollection() From {{"E", "55"}, {"A", "11"}, {"D", "44"}, {"C", "33"}, {"G", "66"}, {"B", "22"}}
Dim dictStrings As Dictionary(Of String, String) = nvc.Cast(Of String).ToDictionary(Function(key) key, Function(key) nvc(key))
Dim Ints2Chars__ As Dictionary(Of Integer, Char) = nvc.Cast(Of Object).ToDictionary(Function(key) CInt(nvc(CStr(key))), Function(key) CChar(key))
Dim arrEnumerable__ = From x In nvc.Cast(Of String) Select {x, nvc(x)}
Dim tupleEnumerable = From x In nvc.Cast(Of String) Select Tuple.Create(x, nvc(x))
Dim anonEnumerable_ = From X In nvc.Cast(Of String) Select New With {X, .Y = nvc(X)}
Dim kvpEnumerable__ = From x In nvc.Cast(Of String) Select New KeyValuePair(Of String, String)(x, nvc(x))
Dim anonQuery = From anon In anonEnumerable_ Let n = CInt(anon.Y) Order By n Where n > 30 Select New With {.num = n, .val = anon.X}
Dim dictQuery = anonQuery.ToDictionary(Of Integer, String)(Function(o) o.num, Function(o) o.val)
Dim dictArray_ = arrEnumerable__.ToDictionary(Function(x) x(0), Function(x) x(1))
Dim dictTuples = tupleEnumerable.ToDictionary(Function(tuple) tuple.Item1, Function(tuple) tuple.Item2)
Dim dictAnon__ = anonEnumerable_.ToDictionary(Function(anon) anon.X, Function(anon) anon.Y)
Dim dictKVPrs_ = kvpEnumerable__.ToDictionary(Function(kvp) kvp.Key, Function(kvp) kvp.Value)