I was recently investigating how to sort a filtered grid of items on one of our websites, Aiveo.  If the user sorted the grid after a filter was applied, the filter was partially or completely lost, and the user would have to manually select the options again.  This issue was caused by incorrectly generating the query string in the URL.  The sorting parameters were correct, but any parameter that consisted of multiple values was wrong.  Turns out fixing this wasn't so simple in ASP.NET by default.

The Problem

The query string we generate for a list of values is serialized as the same parameter with multiple values as in: "m=12&m=13&m=89".  This URL is perfectly valid for a parameter 'm' that is a list of integers.  However, when accessing 'm' in the current request's query parameters, it is serialized as a comma separated list of values like: "12,13,89".  When creating a new URL, the query string we generated contained "m=12%2C13%2C89" (the ',' is encoded as html into "%2C"). This caused Aiveo to "forget" filters because it interprets 'm' as a string, not a list of integers like before.


After investigating possible solutions, I came up with a few options.

1. Remove the list parameter and add each value separately as an indexed value in an array (ie: "m[0]=12").  While this solution works, the URL that is generated is difficult to interpret because it encodes the bracket characters in the URL just like the commas.

2. Construct the URL's query string manually by detecting the list values and generating a string that matches it.

3. Use an IModelUnbinder<T> with T4MVC.  Since we already use a "Model Binder" to construct the server-side model for the URL, this solution makes sense but has the same issue as option #1. 

Final Solution

I chose to parse the parameters in the request, find any list values, and construct the query string manually.  A simplified version of the code that was used is shown below.

private string GenerateCurrentRequestQueryString(HtmlHelper html)
    var parameters = html.ViewContext.HttpContext.Request.QueryString;
    var result = string.Empty;
    var separator = '?';
    foreach (var parameterName in parameters.AllKeys)
        var parameterValues = parameters[parameterName].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
        foreach (var parameterValue in parameterValues)
            result += $"{separator}{parameterName}={parameterValue}";
            separator = '&';
    return result;

Essentially the code goes through the parameters in the current request as determined by ASP.NET and constructs the entire URL string.  When a parameter that appears to be a list is detected by splitting on the comma character, multiple parameters with the same name are appended to the query string.  

That's it!  Now you have the correct query string for the current request.  One thing to note about this function is it will not correctly handle a parameter value that has a comma character since it will be seen as a list of values instead of one value.

Thanks for reading, and if you have any comments or questions, let me know!