86

I have this code:

NameValueCollection nv = HttpUtility.ParseQueryString(queryString);        
foreach (KeyValuePair<String,String> pr in nv) {
    //process KeyValuePair          
}

This compiles, but when I try to run it I get an InvalidCastException.

Why is this? Why can't I use KeyValuePair to iterate over a NameValueCollection, and what should I use instead?

Oliver
  • 11,297
  • 18
  • 71
  • 121
  • I like this as I can init a dictionary without having to create a backing dictionary variable in addition to a foreach. `var nv = HttpUtility.ParseQueryString(Request.Url.Query); var qsDic = nv.Cast().ToDictionary(key => (string) key, key => nv[(string) key]);` – The Muffin Man Sep 25 '13 at 17:36

10 Answers10

143

First of all, NameValueCollection doesn't use KeyValuePair<String,String>. Also, foreach only exposes the key:

NameValueCollection nv = HttpUtility.ParseQueryString(queryString);        
foreach (string key in nv) {
    var value = nv[key];

}
jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • 5
    Hmm, that's kind of weird. I would have expected there to be a way to get both at once. – Oliver Dec 05 '11 at 13:09
  • 29
    We all do when we start using a NVC. – jgauffin Dec 05 '11 at 13:12
  • 1
    Was wondering how to do this... thanks! Btw, you don't need to specify `KeyValuePair` as the NameValueCollection only uses string,string. No need to specify it. – TheGateKeeper Apr 29 '12 at 11:27
  • If you want more control, you can create your own object (which holds a key and a value) and put it in a list, then foreach on your object. – TheGateKeeper Apr 29 '12 at 11:28
  • 1
    ehh? I said that the NVC do not use KeyValuePair? – jgauffin Apr 29 '12 at 11:48
  • 7
    Be aware how this code functions for duplicate keys. When duplicate keys exist, it will return "key value1,value2,value3", instead of "key value1" then "key value2" then "key value3". – Doug S Dec 07 '12 at 01:49
  • This new way seems dumb. I'd really like to know why this is used instead of say a Dictionary so that we could use KeyValuePair. In other words what problem(s) does NameValueCollection solve over Dictionary? – The Muffin Man Sep 25 '13 at 17:32
  • 3
    @Nick: NameValueCollection is one of the first collection types in .NET. It's case insensitive and merge existing values (which is useful for some protocols) – jgauffin Sep 25 '13 at 17:43
  • This solution will not work as expected if you have duplicate keys (NameValueCollection supports duplicate keys). Use Endy Tjahjono's solution instead. – Pavel Melnikov Nov 17 '15 at 10:41
14

You can't do that directly, but you can create an extension method like so:

public static IEnumerable<KeyValuePair<string, string>> AsKVP(
        this NameValueCollection source
)
{
    return source.AllKeys.SelectMany(
        source.GetValues,
        (k, v) => new KeyValuePair<string, string>(k, v));
}

Then you can do:

NameValueCollection nv = HttpUtility.ParseQueryString(queryString);
foreach (KeyValuePair<String,String> pr in nv.AsKVP()) {
    //process KeyValuePair          
}

Note: inspired by this. SelectMany is required to handle duplicate keys.

vb.net version:

<Extension>
Public Function AsKVP(
        source As Specialized.NameValueCollection
) As IEnumerable(Of KeyValuePair(Of String, String))
    Dim result = source.AllKeys.SelectMany(
        AddressOf source.GetValues,
        Function(k, v) New KeyValuePair(Of String, String)(k, v))
    Return result
End Function
Community
  • 1
  • 1
Endy Tjahjono
  • 24,120
  • 23
  • 83
  • 123
10

For future reference, you could also use this syntax:

foreach(string key in Request.QueryString)
{
    var value = Request.QueryString[key];
}
jgauffin
  • 99,844
  • 45
  • 235
  • 372
Sebastien Morin
  • 151
  • 2
  • 3
  • 2
    Well. If you got access to the `HttpRequest` you can skip `AllKeys` as the `QueryString` property is a `NameValueCollection`. – jgauffin May 21 '14 at 15:12
  • -1 because you don't have to use `AllKeys` as @jgauffin mentioned – Warlock Jul 26 '14 at 08:46
  • @Warlock: Giving a -1 for that is a bit harsh. You can edit the answer, can't you? – jgauffin Jul 26 '14 at 10:37
  • Yes, sorry for that, I didn't want to hurt anybody :) But the problem was not only in the property. If you look at the @jgauffin's answer, he proposed the same idea, but it was posted earlier. So, this post looks like a duplication. – Warlock Jul 26 '14 at 16:01
  • If you look at the last comment it's written by me `jgauffin` and the answers are not duplicates if you look closer. – jgauffin Jul 26 '14 at 16:02
  • Cool :) But I still can't see the difference. The same `NameValueCollection` with the same `foreach`. Your answer is more complete btw. – Warlock Jul 26 '14 at 16:08
  • My answer can parse any string while his uses a already parsed query string. imho his answer works better when wanting the query from the current http request. – jgauffin Jul 26 '14 at 16:16
6

ANother extension method, for learning purposes:

    public static IEnumerable<KeyValuePair<string, string>> ToIEnumerable(this NameValueCollection nvc)
    {
        foreach (string key in nvc.AllKeys)
        {
            yield return new KeyValuePair<string, string>(key, nvc[key]);
        }
    }
Adriano Carneiro
  • 57,693
  • 12
  • 90
  • 123
  • Nice solution! I would just suggest to use `nvc` instead of `nvc.AllKeys`. I'm wondering why Microsoft guys didn't add this in `System.Linq`. :) – Warlock Jul 26 '14 at 08:48
2

NameValueCollection uses the old-skool enumerator:

        var enu = ConfigurationManager.AppSettings.GetEnumerator();

        while(enu.MoveNext())
        {
            string key = (string)enu.Current;
            string value = ConfigurationManager.AppSettings[key];
        }
DavidWainwright
  • 2,895
  • 1
  • 27
  • 30
0

I did like this and it works:

 foreach (string akey in request.Query.Keys.Cast<string>())
     writer.WriteLine(akey + " = " + request.Query[akey]);
0

Be aware that the key name might appear more than once in the query string and that the comparison is usually case sensitive.
If you want to just get the value of the first matching key and not bothered about case then use this:

        public string GetQueryValue(string queryKey)
        {
            foreach (string key in QueryItems)
            {
                if(queryKey.Equals(key, StringComparison.OrdinalIgnoreCase))
                    return QueryItems.GetValues(key).First(); // There might be multiple keys of the same name, but just return the first match
            }
            return null;
        }
userSteve
  • 1,554
  • 1
  • 22
  • 34
0

To convert nameValueCollection from QueryString to List you can use extension like this:

public static List<KeyValuePair<string, string>> GetParams(this HttpRequest request)
{
    var nameValueCollection = HttpUtility.ParseQueryString(request.QueryString.Value);
    List<KeyValuePair<string, string>> keyValueCollections = new List<KeyValuePair<string, string>>();
    if (!nameValueCollection.IsNullOrEmpty())
    {
        foreach (var key in nameValueCollection.AllKeys)
        {
            keyValueCollections.Add(new KeyValuePair<string, string>(key, nameValueCollection[key]));
        }
    }
    return keyValueCollections;
}
jasmin
  • 171
  • 1
  • 5
0

How about using the built in .AsEnumerable() extension method? Example:

NameValueCollection nv = HttpUtility.ParseQueryString(queryString);        
foreach (KeyValuePair<String,String> pr in nv.AsEnumerable()) {
    //process KeyValuePair          
}
Jakob Bagterp
  • 450
  • 4
  • 12
-1
public static void PrintKeysAndValues2( NameValueCollection myCol )
{
    Console.WriteLine( "   [INDEX] KEY        VALUE" );
    for ( int i = 0; i < myCol.Count; i++ )
        Console.WriteLine( "   [{0}]     {1,-10} {2}", i, myCol.GetKey(i), myCol.Get(i) );
    Console.WriteLine();
}

http://msdn.microsoft.com/en-us/library/system.collections.specialized.namevaluecollection.aspx

  • -1 You show that it is possible to use for to access `NameValueCollection` data. But the question is why you cannot convert `NameValueCollection` items to `KeyValuePair`. Maybe you read the question not very carefully. The best answer was given by `jgauffin`. An interesting solution was proposed by `Adrian Carneiro`. – Warlock Jul 26 '14 at 16:15