ASP.Net MVC RouteData e array

Se ho un’azione come questa:

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

Posso colpirlo con il seguente URL:

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

Ma nel mio ViewPage, ho questo codice:

  

Sfortunatamente, MVC non è abbastanza intelligente da riconoscere che l’azione richiede un array e srotola l’elenco per formare la giusta route url. invece fa semplicemente un .ToString () sull’object che elenca solo il tipo di dati nel caso di un elenco.

C’è un modo per ottenere Html.ActionLink per generare un URL appropriato quando uno dei parametri di azione di destinazione è un array o un elenco?

— modificare —

Come Josh ha sottolineato di seguito, ViewData [“stuff”] è solo un object. Ho provato a semplificare il problema ma invece ho causato un bug non correlato! In realtà sto usando un ViewPage dedicato , quindi ho un modello sensibile al tipo strettamente connesso. In realtà, ActionLink assomiglia a:

  

Dove ViewData.Model.Stuff è stato digitato come elenco

Sto pensando che un HtmlHelper personalizzato sarebbe in ordine.

  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 ); } 

puoi suffisso i tuoi routevalues con un indice di array in questo modo:

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

ciò comporterà la querystring contenente test=val1&test=val2

quello potrebbe aiutare?

La combinazione di entrambi i metodi funziona bene.

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

Quindi utilizzare questo metodo in qualsiasi metodo di estensione che richiede routeValues ​​con IEnumerable (s) in esso.

Purtroppo, questa soluzione alternativa è necessaria anche in MVC3.

Esiste un librarly chiamato Unbinder , che puoi usare per inserire oggetti complessi in route / urls.

Funziona così:

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

Questo fungerà solo da estensione a UrlHelper e fornirà semplicemente un bel URL pronto per essere messo ovunque anziché un intero tag, inoltre conserverà la maggior parte degli altri valori di route per qualsiasi altro URL specifico utilizzato … dandoti l’url specifico più amichevole che hai (meno i valori IEnumerable) e poi aggiungi i valori della stringa di query alla fine.

 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())); } 

Non sono al mio posto di lavoro, ma che ne dici di qualcosa come:

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

o il digitato:

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