65

NameValueCollectionwhere、join、groupbyなどのLINQクエリ演算子にアクセスできるようにするにはどうすればよいですか?

私は以下を試しました:

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

しかし、ソースがIEnumerable<>ではないことを通知するArgumentExceptionが発生しました。

4

7 に答える 7

96

非ジェネリックIEnumerableをに「持ち上げる」必要がありIEnumerable<string>ます。使用することが提案されていますOfTypeが、それはフィルタリング方法です。あなたがしていることは、Cast演算子があるキャストと同等です:

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

Frans が指摘したように、これはキーへのアクセスのみを提供します。値のコレクションにインデックスを付ける必要があります。KeyValuePairからを抽出する拡張メソッドを次に示し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]));
}

編集: @Ruben Bartelink のリクエストに応えて、以下を使用して各キーの値の完全なセットにアクセスする方法を次に示します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);
}

または、C# 7.0 タプルを使用します。

public static IEnumerable<(String name, String value)> ToTuples(this NameValueCollection collection)
{
    if(collection == null)
    {
        throw new ArgumentNullException("collection");
    }

    return
        from key in collection.Cast<string>()
        from value in collection.GetValues(key)
        select (key, value);
}
于 2008-12-28T18:38:03.803 に答える
11

AsQueryableIEnumerable<T>、ジェネリックを取る必要があります。NameValueCollectionを実装しますIEnumerableが、これは異なります。

これの代わりに:

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

OfTypeを試してください(非ジェネリックインターフェイスを受け入れます)

{
    NameValueCollection nvc = RequestFields();
    IEnumerable<string> canBeQueried = nvc.OfType<string>();
    IEnumerable<string> query =
       canBeQueried.Where(s => s.StartsWith("abc"));
}
于 2008-12-24T15:15:22.873 に答える
8

ディクショナリは、NameValueCollection が果たすよりも多くの役割を実際に果たすため、おそらく実際に使用したいものに近いでしょう。これは、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); 
    }
}
于 2010-06-07T15:21:26.680 に答える
4

IEnumerable問題は、コレクションが(ではなく)実装し、コレクションIEnumerable<T>を列挙すると、ペアではなくキーが返されることです。

私があなたなら、Dictionary<string, string>列挙可能でLINQで使用できるaを使用します。

于 2008-12-24T08:36:52.320 に答える
3

私にとって、@ Bryan Watts '(+1'd)の回答のToLookupバリアントは、読み取り専用で使用するための最も明確なアプローチを表しています。

私のユースケースでは、Linq2Restで使用するためにクエリ文字列を操作しており、最後にすべてを に戻す必要があるため、より詳細な操作を提供するNameValueCollection拡張メソッドのセットがあります (パラメーターごとに両方を操作するため)。 NameValueCollectionname ( AsEnumerable) および per argument ( AsKeyValuePairs)) と、それを元に戻す逆の操作ToNameValueCollection(いずれかの表現から))。

消費例:

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

コード:

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;
    }
}
于 2012-10-03T13:01:53.283 に答える