.net linq namevaluecollection

.net - Hacer que NameValueCollection sea accesible para LINQ Query



(7)

El problema es que la colección implementa IEnumerable (a diferencia de IEnumerable<T> ) y enumerar la colección devuelve las claves, no los pares.

Si yo fuera usted, usaría un Dictionary<string, string> que es enumerable y puede usarse con LINQ.

¿Cómo hacer que NameValueCollection accesible para el operador de consultas LINQ como where, join, groupby?

Intenté lo siguiente:

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

Pero tengo una ArgumentException que me dice que la fuente no es IEnumerable <> .


Es probable que un diccionario esté más cerca de lo que desea usar, ya que en realidad cumplirá más funciones que NameValueCollection. Esta es una variación de la solución 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); } }


Para mí, la variante ToLookup la respuesta de @Bryan Watts (+1 en inglés) representa, de lejos, el enfoque más claro para usarlo solo como lectura.

Para mi caso de uso, estoy manipulando una cadena de consulta para usar con Linq2Rest y también necesito convertir todo de nuevo en una NameValueCollection al final, así que tengo un conjunto de métodos de extensión para NameValueCollection que ofrecen operaciones más granulares (para operar ambos por nombre de parámetro ( AsEnumerable ) y por argumento ( AsKeyValuePairs )) y también la operación inversa de convertirlo nuevamente a ToNameValueCollection (de cualquier representación)).

Consumo de ejemplo:

public static NameValueCollection WithoutPagingOperators( this NameValueCollection that ) { return that.AsEnumerable() .Where( @param => @param.Key != OdataParameters.Skip && @param.Key != OdataParameters.Top ) .ToNameValueCollection(); }

Código:

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


Realmente no veo por qué alguien debería agregar un método de extensión.
Aquí hay algunas maneras diferentes de hacerlo en VB.NET. Incluye 4 formas intermedias diferentes de IEnumerable: Array, Tuple, Anonymous y KeyValuePair. Para el equivalente de C # ve a converter.telerik dot com y conviértelo.

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)


Sé que llego tarde a la fiesta, pero solo quería agregar mi respuesta que no incluye el método de extensión .Cast , sino que utiliza la propiedad AllKeys:

var fields = RequestFields().AllKeys;

Esto permitiría el siguiente método de extensión:

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

Espero que esto ayude a cualquier visitante futuro


AsQueryable debe tomar un IEnumerable<T> , un genérico. NameValueCollection implementa IEnumerable , que es diferente.

En lugar de esto:

{ NameValueCollection nvc = RequestFields(); IQueryable queryable = nvc.AsQueryable(); }

Pruebe OfType (acepta la interfaz no genérica)

{ NameValueCollection nvc = RequestFields(); IEnumerable<string> canBeQueried = nvc.OfType<string>(); IEnumerable<string> query = canBeQueried.Where(s => s.StartsWith("abc")); }


IEnumerable "elevar" el IEnumerable no genérico a una IEnumerable<string> . Se ha sugerido que use OfType pero ese es un método de filtrado. Lo que estás haciendo es el equivalente de un elenco, para el cual está el operador de Cast :

var fields = RequestFields().Cast<string>();

Como señaló Frans, esto solo proporciona acceso a las claves. Aún necesitaría indexar en la colección para los valores. Aquí hay un método de extensión para extraer KeyValuePair s de 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])); }

Editar: en respuesta a la solicitud de @Ruben Bartelink, aquí se muestra cómo acceder al conjunto completo de valores para cada tecla usando 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); }