如何使用具有多个同名值的RouteValues

时间:2015-02-12 16:02:39

标签: c# asp.net-mvc-4 routevalues

在我的ASP.NET MVC 4应用程序中,我可以过滤多个标签。在HTML中,它看起来像这样:

<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>

当检查第一个和第三个复选框时,查询字符串被序列化为?tag=1&tag=3,我的控制器很好地传递了具有以下类类型的对象:

// 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"
    ...
 }

但是,对Url.Action的调用会导致序列化集合的类型名称,而不是实际值。

如何做到这一点?


MVC的标准基础结构可以处理描述为输入的多键。是否没有标准的基础设施能够以相反的方式处理它?我错过了什么吗?

4 个答案:

答案 0 :(得分:4)

您可以通过以下方式完成此操作:

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

TypeHelper2.ObjectToDictionary是.NET内部方法的修改版本,可在此two file gist中找到。

更改行为:当项目实施IEnumerable时,每个项目都有一个条目 在返回的字典中使用键"Name[index]"创建(索引基于0)。这是可能的,因为MVC控制器的绑定器可以处理tag=1&tag=3tag[0]=1&tag[1]=3个查询字符串。

答案 1 :(得分:3)

一个简单但不那么优雅的解决方案可以是:

  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;

  }

答案 2 :(得分:2)

赞成以上2个答案 - 他们都是非常好的答案。在这方面(重定向到一个动作或生成一个URL重定向到)MVC根本不“喜欢”数组。

我可以看到两种进一步的方法:

1)使用TempData来持久检索数组(例如this article的底部)

2)为AdFilter编写和使用自定义模型绑定器

我自己会选择后者 - 可测试且更具确定性(另外,当您进入服务器场场景时,我不知道TempData如何工作)

您可能想要考虑的另一件事是使用类似于Index操作的返回。

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

(这将返回Dofilter视图,同时在浏览器中保持“index?tag [0] = 1&amp; tag 1 = 2”url)

最后 - 感觉您只是将过滤条件发送回浏览器,以便浏览器再次询问过滤结果。也许更好的选择是将表单上的操作设置为从一开始就发布到“DoFilter”:

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

HTH

答案 3 :(得分:1)

您使用接受对象的Url.Action()重载来生成路由参数。在内部,这通过使用反射来构建每个属性名称及其.ToString()值的字典。

在简单值类型的情况下,这可能是Name = "doekman"(变为/DoFilter?Name=doekman),但在属性是复杂对象或集合的情况下,它返回类型名称(即.ToString()值。没有进行递归,这是有道理的,因为查询字符串有长度限制,所以如果在复杂的对象和集合上进行递归,你很快就会超出限制并在集合包含大量项目时抛出异常(并且它会创建一个真正的丑陋的查询字符串)。

所以回答How can this be done?除非你手动生成RouteValueDictionary(或编写自己的助手来生成查询字符串),否则你不能这样做。