Search code examples
c#asp.net-mvc-4routevalues

How to work with RouteValues with multiple values of the same name


In my ASP.NET MVC 4 application I can filter on multiple tags. In HTML, it looks like this:

<form>
  <label>
    <input type="checkbox" name="tag" value="1">One
  </label>
  <label>
    <input type="checkbox" name="tag" value="2">Two
  </label>
  <label>
    <input type="checkbox" name="tag" value="3">Three
  </label>
  <input type="submit" name="action" value="Filter">
</form>

When checking the first and third checkbox, the querystring is serialized as ?tag=1&tag=3 and my controller nicely passes an object with the type of the following class:

// Filter class
public class Filter { 
    public ICollection<int> tag { get; set; }
}

// Controller method
public ActionResult Index(AdFilter filter)
{
    string url = Url.Action("DoFilter", filter);
    // url gets this value:
    // "/controller/Index?tag=System.Collections.Generic.List%601%5BSystem.Int32%5D"
    // I would expect this:
    // "/controller/Index?tag=1&tag=3"
    ...
 }

However, a call to Url.Action results in the typename of the collection being serialized, instead of the actual values.

How can this be done?


The standard infrastructure of MVC can handle the multi-keys described as input. Is there not standard infrastructure that can handle it the other way around? Am I missing something?


Solution

  • You can do it in the following way:

    string url = Url.Action("DoFilter", TypeHelper2.ObjectToDictionary(filter) );
    

    The TypeHelper2.ObjectToDictionary is a modified version of an internal method of .NET, and can be found in this two file gist.

    Changed behavior: when an item implements IEnumerable, for each item an entry is created in the returned dictionary with as key "Name[index]" (the index is 0 based). This is possible because the binder of the MVC controller can handle both tag=1&tag=3 and tag[0]=1&tag[1]=3 query strings.