【问题标题】:Make NameValueCollection accessible to LINQ Query使 LINQ 查询可以访问 NameValueCollection
【发布时间】:2008-12-24 08:28:47
【问题描述】:

如何使NameValueCollection 可供LINQ 查询运算符(例如where、join、groupby)访问?

我尝试了以下方法:

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

但我得到一个 ArgumentException 告诉我源不是 IEnumerable

【问题讨论】:

标签: .net linq namevaluecollection


【解决方案1】:

您需要将非泛型IEnumerable“提升”为IEnumerable<string>。有人建议您使用OfType,但这是一种过滤方法。你所做的相当于一个演员表,其中有 Cast 运算符:

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

正如 Frans 所指出的,这仅提供对密钥的访问。您仍然需要对值的集合进行索引。这是从NameValueCollection 中提取KeyValuePairs 的扩展方法:

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

【讨论】:

  • 如何使用 Where 子句?和 lambda?
  • 警告。通过这种方式,您可能会错过一些值。一个键可以分配多个值,请参见 GetValues 方法。
  • @Kugel:很大的区别——我没有意识到NameValueCollection 实际上支持每个键的多个值。 Item 属性 (msdn.microsoft.com/en-us/library/8d0bzeeb.aspx) 的文档说它将返回逗号分隔列表中的值。因此,您不会完全丢失它们,但它们不会采用您可能期望的键/值格式。
  • -1 修复代码以使用.ToLookup 或产生数组或添加标记逗号分隔列表问题的编辑的任何机会。修复后将删除,我再次看到这个。 (在撰写本文时,这里的每个答案都有不好的建议,这很烦人)
  • ToLookup中的Linq表达式可以写成collection.Cast&lt;string&gt;().SelectMany(key =&gt; collection.GetValues(key), (key, value) =&gt; new {key, value})。如果你想要 lambdas :)
【解决方案2】:

AsQueryable 必须采用IEnumerable&lt;T&gt;,一个泛型。 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"));
}

【讨论】:

  • 很好的解决方案!我需要一种简单的方法来在 Request.Params NameValueCollection 中搜索特定模式,而这一点代码帮助我实现了目标。
  • -1 如其他答案所述,应该是 Cast,而不是 OfType,如果我再次看到它并且文本中没有 OfType,评论和 downvote 就会消失。
  • @Ruben Bartelink,我支持我使用 OfType。 Cast 不可靠,因为它已在不同的 .net 版本之间进行了修改。
  • 任何引用?你觉得里面还有什么? (Obv 如果这可以被证实是真实的,那么这有真正的价值,我的投票会翻转)
  • social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/… 来自 Heljsberg:“所有这些都已在 SP1 中修复”
【解决方案3】:

我知道我迟到了,但只是想添加我的答案,不涉及 .Cast 扩展方法,而是使用 AllKeys 属性:

var fields = RequestFields().AllKeys;

这将允许以下扩展方法:

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

希望这对未来的访问者有所帮助

【讨论】:

  • 这看起来是一种不错的实现方式。 +1 使用
【解决方案4】:

字典实际上可能更接近您想要使用的内容,因为它实际上会填补 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); 
    }
}

【讨论】:

  • 仍然不是很好。 NameValueCollection 实际上大致是IDictionary&lt;string,IEnumerable&lt;string&gt;&gt;。对于一些应该保留的用例。 ILookup 是您真正需要的。像souce.Cast&lt;string&gt;().SelectMany(s =&gt; source.GetValues(s).Select(t=&gt; new {Key=s, Value=t}) ).ToLookup(p=&gt;p.Key,p=&gt;p.Value) 这样的东西。这将与 Linq 很好地配合,因为 ILookup 是专门为 Linq 创建的。
  • -1 这只是@Frans Bouma 的 impl 的一个特定 impl,具有明显的副作用是压缩用逗号分隔的值。使用 ToLookup 时为 -1
【解决方案5】:

问题在于集合实现了IEnumerable(而不是IEnumerable&lt;T&gt;)并且枚举集合返回键,而不是对。

如果我是你,我会使用 Dictionary&lt;string, string&gt;,它是可枚举的并且可以与 LINQ 一起使用。

【讨论】:

  • 谢谢,但我不太明白为什么尽管 NameValueCollection 实现了 IEnumerable,但它仍然说 source 不是 IEnumerable。
  • 它实现了 IEnumerable(在基类上)但它没有实现 IEnumerable(泛型变体),因此您不能使用需要泛型变体的扩展方法。
  • +1 但正如@Kevin Cathcart 在 Orion Adrian 的回答中指出的那样,如果您实际上扁平化为 Dictionary&lt;key,string&gt;,则多个值会用逗号连接,这会使操作有损
【解决方案6】:

对我来说,@Bryan Watts (+1'd) 答案的 ToLookup 变体代表了迄今为止在只读基础上使用它的最清晰的方法。

对于我的用例,我正在处理与Linq2Rest 一起使用的查询字符串,并且还需要在最后将其全部转回NameValueCollection,因此我有一组NameValueCollection 的扩展方法它提供了更精细的操作(对每个参数名称 (AsEnumerable) 和每个参数 (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;
    }
}

【讨论】:

    【解决方案7】:

    我真的不明白为什么有人需要添加扩展方法。
    以下是在 VB.NET 中执行此操作的一些不同方法。它包括 4 种不同的 IEnumerable 中间形式:Array、Tuple、Anonymous 和 KeyValuePair。对于 C# 等效项,请访问 converter.telerik dot com 并对其进行转换。

    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)
    

    【讨论】:

      猜你喜欢
      • 2011-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-06
      • 1970-01-01
      相关资源
      最近更新 更多