ASP.Net MVC RouteData和数组
如果我有这样的动作:
public ActionResult DoStuff(List stuff) { ... ViewData["stuff"] = stuff; ... return View(); }
我可以使用以下url点击它:
http://mymvcapp.com/controller/DoStuff?stuff=hello&stuff=world&stuff=foo&stuff=bar
但在我的ViewPage中,我有这个代码:
不幸的是,MVC不够聪明,无法识别该动作采用数组,并展开列表以形成正确的URL路由。 相反,它只是在对象上执行.ToString(),它只列出了List中的数据类型。
当其中一个目标Action的参数是数组或列表时,有没有办法让Html.ActionLink生成一个正确的URL?
– 编辑 –
正如Josh在下面指出的那样,ViewData [“stuff”]只是一个对象。 我试图简化问题,但引起了一个无关的错误! 我实际上使用的是专用的ViewPage ,因此我有一个紧密耦合的类型感知模型。 ActionLink实际上看起来像:
其中ViewData.Model.Stuff被键入为List
我认为自定义的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 urlParameters = new List (); 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 ); }
你可以使用数组索引为你的routevalues
添加后缀,如下所示:
RouteValueDictionary rv = new RouteValueDictionary(); rv.Add("test[0]", val1); rv.Add("test[1]", val2);
这将导致包含test=val1&test=val2
的查询字符串
那可能有帮助吗?
结合这两种方法很有效。
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的routeValues的扩展方法中使用此方法。
遗憾的是,这种解决方法也需要在MVC3中使用。
有一个名为Unbinder的库 ,可用于将复杂对象插入到路径/ URL中。
它的工作原理如下:
using Unbound; Unbinder u = new Unbinder(); string url = Url.RouteUrl("routeName", new RouteValueDictionary(u.Unbind(YourComplexObject)));
这只是作为UrlHelper
的扩展,只是提供一个很好的url,可以放在任何地方而不是整个标签,它也将保留大部分其他路由值,用于任何其他特定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())); }
我不在我的工作站,但是如下:
<%= Html.ActionLink("click here", "DoMoreStuff", "MoreStuffController", new { stuff = (List)ViewData["stuff"] }, null) %>
或打字:
<%= Html.ActionLink("click here", "DoMoreStuff", "MoreStuffController", new { stuff = (List)ViewData.Model.Stuff }, null) %>