【问题标题】:ASP.Net MVC RouteData and arraysASP.Net MVC RouteData 和数组
【发布时间】:2009-11-17 23:56:31
【问题描述】:

如果我有这样的动作:

public ActionResult DoStuff(List<string> stuff)
{
   ...
   ViewData["stuff"] = stuff;
   ...
   return View();
}

我可以通过以下网址点击它:

http://mymvcapp.com/controller/DoStuff?stuff=hello&stuff=world&stuff=foo&stuff=bar

但在我的 ViewPage 中,我有以下代码:

<%= Html.ActionLink("click here", "DoMoreStuff", "MoreStuffController", new { stuff = ViewData["stuff"] }, null) %>

不幸的是,MVC 不够聪明,无法识别该操作需要一个数组,并展开列表以形成正确的 url 路由。相反,它只是在对象上执行 .ToString() ,在 List 的情况下只列出数据类型。

当目标 Action 的参数之一是数组或列表时,有没有办法让 Html.ActionLink 生成正确的 URL?

-- 编辑--

正如 Josh 在下面指出的,ViewData["stuff"] 只是一个对象。我试图简化问题,但却导致了一个不相关的错误!我实际上使用的是专用的 ViewPage 所以我有一个紧密耦合的类型感知模型。 ActionLink 实际上看起来像:

<%= Html.ActionLink("click here", "DoMoreStuff", "MoreStuffController", new { stuff = ViewData.Model.Stuff }, null) %>

ViewData.Model.Stuff 被键入为 List

【问题讨论】:

  • ViewData["stuff"] 只是一个对象。当你传入一个真实的列表时会发生什么,比如 {Stuff= (List)ViewData["stuff"]} 或 {Stuff= ViewData["stuff"] as List} 或 {Stuff= new List(...)}?
  • 同样的问题...在我目前的实际实现中,我使用紧密耦合的 ViewPage ,因此该行看起来更像: 其中 ViewData.Model.Stuff 被键入为 List

标签: c# asp.net-mvc arrays list asp.net-mvc-routing


【解决方案1】:

我认为自定义 HtmlHelper 应该是合适的。

 public static string ActionLinkWithList( this HtmlHelper helper, string text, string action, string controller, object routeData, object htmlAttributes )
 {
     var urlHelper = new UrlHelper( helper.ViewContext.RequestContext );


     string href = urlHelper.Action( action, controller );

     if (routeData != null)
     {
         RouteValueDictionary rv = new RouteValueDictionary( routeData );
         List<string> urlParameters = new List<string>();
         foreach (var key in rv.Keys)
         {
             object value = rv[key];
             if (value is IEnumerable && !(value is string))
             {
                 int i = 0;
                 foreach (object val in (IEnumerable)value)
                 {
                     urlParameters.Add( string.Format( "{0}[{2}]={1}", key, val, i ));
                     ++i;
                 }
             }
             else if (value != null)
             {
                 urlParameters.Add( string.Format( "{0}={1}", key, value ) );
             }
         }
         string paramString = string.Join( "&", urlParameters.ToArray() ); // ToArray not needed in 4.0
         if (!string.IsNullOrEmpty( paramString ))
         {
            href += "?" + paramString;
         }
     }

     TagBuilder builder = new TagBuilder( "a" );
     builder.Attributes.Add("href",href);
     builder.MergeAttributes( new RouteValueDictionary( htmlAttributes ) );
     builder.SetInnerText( text );
     return builder.ToString( TagRenderMode.Normal );
}

【讨论】:

  • 是的,这肯定会奏效。更一般地说,在 UrlHelper.Action 之类的 Url 生成器和操作参数绑定之间应该有什么级别的兼容性?我的意思是,这是一个错误还是什么?
  • 没有。不是错误。我称之为设计决策。参数有多个值的情况非常罕见,尤其是在获取时。我认为对象是简单值类型映射的期望对大多数人来说都很好。
  • 谢谢你,它让我的想法朝着正确的方向发展。它不会完全起作用,因为字符串是一个 IEnumerable,它将枚举字符。它还使所有路由参数成为查询字符串参数,而原始 ActionLink 考虑了在 global.asax.cs 中创建的路由来格式化支持它的路由参数 REST 样式。
  • 我接受了你的想法,我使用 LINQ 为我提供了所有作为 IEnumerable.. 的路由数据项,并用它生成了一个部分查询字符串。然后,我获取了剩余的路线数据,并向其中添加了另一个条目,例如“replacemequerystringname”、“replacemequerystringvalue”然后,我将常规 ActionLink 方法与剩余的路线数据一起使用。然后我用我前面提到的部分查询字符串替换上面已经转换为查询字符串参数的路由条目
  • 嗯。也许检查它是否实现了 IList 而不是 IEnumerable?或者专门排除字符串。
【解决方案2】:

您可以在 routevalues 后面加上一个数组索引,如下所示:

RouteValueDictionary rv = new RouteValueDictionary();
rv.Add("test[0]", val1);
rv.Add("test[1]", val2);

这将导致查询字符串包含test=val1&amp;test=val2

这可能有帮助吗?

【讨论】:

  • 谢谢你,这太棒了......哦等等,没关系,它没有按预期工作
  • @JarrettV Url 似乎没问题,它只是编码(并且应该是)
【解决方案3】:

结合这两种方法效果很好。

public static RouteValueDictionary FixListRouteDataValues(RouteValueDictionary routes)
{
    var newRv = new RouteValueDictionary();
    foreach (var key in routes.Keys)
    {
        object value = routes[key];
        if (value is IEnumerable && !(value is string))
        {
            int index = 0;
            foreach (string val in (IEnumerable)value)
            {
                newRv.Add(string.Format("{0}[{1}]", key, index), val);
                index++;
            }
        }
        else
        {
            newRv.Add(key, value);
        }
    }

    return newRv;
}

然后在任何需要包含 IEnumerable(s) 的 routeValues 的扩展方法中使用此方法。

遗憾的是,MVC3 也需要这种解决方法。

【讨论】:

    【解决方案4】:

    有一个名为Unbinder 的库,您可以使用它来将复杂的对象插入到路由/网址中。

    它是这样工作的:

    using Unbound;
    
    Unbinder u = new Unbinder();
    string url = Url.RouteUrl("routeName", new RouteValueDictionary(u.Unbind(YourComplexObject)));
    

    【讨论】:

      【解决方案5】:

      这将只是作为UrlHelper 的扩展,并且只是提供一个可以放置在任何地方的好 url,而不是一个完整的 a 标签,它还将保留大多数其他正在使用的特定 url 的路由值... 为您提供最友好的特定 url(减去 IEnumerable 值),然后在末尾附加查询字符串值。

      public static string ActionWithList(this UrlHelper helper, string action, object routeData)
      {
      
          RouteValueDictionary rv = new RouteValueDictionary(routeData);
      
          var newRv = new RouteValueDictionary();
          var arrayRv = new RouteValueDictionary();
          foreach (var kvp in rv)
          {
              var nrv = newRv;
              var val = kvp.Value;
              if (val is IEnumerable && !(val is string))
              {
                  nrv = arrayRv;
              }
      
              nrv.Add(kvp.Key, val);
      
          }
      
      
          string href = helper.Action(action, newRv);
      
          foreach (var kvp in arrayRv)
          {
              IEnumerable lst = kvp.Value as IEnumerable;
              var key = kvp.Key;
              foreach (var val in lst)
              {
                  href = href.AddQueryString(key, val);
              }
      
          }
          return href;
      }
      
      public static string AddQueryString(this string url, string name, object value)
      {
          url = url ?? "";
      
          char join = '?';
          if (url.Contains('?'))
              join = '&';
      
          return string.Concat(url, join, name, "=", HttpUtility.UrlEncode(value.ToString()));
      }   
      

      【讨论】:

        【解决方案6】:

        我不在我的工作站,但是怎么样:

        <%= Html.ActionLink("click here", "DoMoreStuff", "MoreStuffController", new { stuff = (List<T>)ViewData["stuff"] }, null) %>
        

        或键入:

        <%= Html.ActionLink("click here", "DoMoreStuff", "MoreStuffController", new { stuff = (List<T>)ViewData.Model.Stuff }, null) %>
        

        【讨论】:

        • 它仍然会在对象上调用 ToString()。
        猜你喜欢
        • 1970-01-01
        • 2023-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-08
        相关资源
        最近更新 更多