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

谁都会走 提交于 2019-12-11 03:09:48

问题


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?


回答1:


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.




回答2:


An easy yet no so elegant solution can be:

  public ActionResult Index(AdFilter filter)
  {
     string parameters = "?";
     foreach (var item in filter.tag)
        {
            parameters += string.Format("tag={0}&", item);
        }
     //trimming the last "&"
     parameters = parameters.TrimEnd(parameters[parameters.Length - 1]);
     string url = Url.Action("DoFilter") + parameters;

  }



回答3:


Have upvoted the above 2 answers - they're both perfectly good answers. On this side of things (redirecting to an action or generaring a URL to redirect to) MVC doesn't "like" arrays at all.

There are 2 futher approaches I can see:

1) use TempData to persist an retrieve the array (e.g at the bottom of this article)

2) Write and use a custom model binder for AdFilter

I'd go for the latter myself - testable and more deterministic (also, I don't know how TempData works when you get into a server farm scenario)

Another thing you might want to consider is using something like as the return from the Index action.

return View("DoFilter", new AdFilter(){tag = tag});

(This will return the Dofilter view whilst maintaining the "index?tag[0]=1&tag1=2" url in the browser)

Finally - it feels like you're taking the filter criteria only to send them back to the browser so the browser can then ask again for the filter results. Maybe a better option would be to set the action on the form to post to "DoFilter" from the start:

<form  method="post" action="DoFilter"> 

HTH




回答4:


You using the overload of Url.Action() that accepts a object to generate the route parameters. Internally this works by using reflection to build a dictionary of each properties name and its .ToString() value.

In the case of a simple value type this might be Name = "doekman" (which becomes /DoFilter?Name=doekman), but in the case of a property which is a complex object or a collection it returns the typename (i.e. the .ToString() value). No recursion is done and this makes sense because query strings have length limits so if recursion was done on complex objects and collections, you would soon exceed the limit and throw an exception if the collection contained a lot of items (and it would create a really ugly query string).

So in answer to How can this be done? You can't unless you manually generate the RouteValueDictionary (or write your own helper to generate the query string)



来源:https://stackoverflow.com/questions/28482103/how-to-work-with-routevalues-with-multiple-values-of-the-same-name

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!