2009-11-17 13 views
23

Jeśli mam działanie takiego:ASP.Net MVC RouteData i tablice

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

mogę uderzyć go z następującym adresem URL:

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

Ale w moim ViewPage, mam ten kod :

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

Niestety MVC nie jest wystarczająco inteligentny, aby uznać, że akcja toczy tablicę i rozwija listę do tworzenia odpowiednich ro url ute. zamiast tego po prostu robi .ToString() na obiekcie, który po prostu wymienia typ danych w przypadku listy.

Czy istnieje sposób, aby Html.ActionLink wygenerował prawidłowy adres URL, gdy jednym z parametrów docelowej akcji jest tablica lub lista?

- edycja -

Jak Josh wskazał poniżej ViewData [ "rzeczy"] to tylko przedmiot. Próbowałem uprościć problem, ale spowodowałem niezwiązany błąd! W rzeczywistości używam dedykowanego ViewPage <T>, więc mam ściśle związany model świadomego modelu. Actionlink faktycznie wygląda:

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

Gdzie ViewData.Model.Stuff jest wpisany jako Lista

+1

ViewData [ "rzeczy"] to tylko przedmiot. Co się stanie, gdy przejdziesz na prawdziwą listę, na przykład {Stuff = (Lista ) ViewData ["stuff"]} lub {Stuff = ViewData ["stuff"] jako List } lub {Stuff = new List (...)}? –

+0

@Josh, dokładnie moje myśli. –

+1

ten sam problem ... W mojej obecnej implementacji używam ściśle powiązanego ViewPage , więc linia wygląda bardziej jak: <% = Html.ActionLink ("kliknij tutaj", "DoMoreStuff", "MoreStuffController", nowy {stuff = ViewData.Model.Stuff}, null)%> gdzie ViewData.Model.Stuff jest wpisany jako lista puffpio

Odpowiedz

19

myślę, że zwyczaj HtmlHelper byłoby w porządku.

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

Tak, to na pewno zadziała. Na bardziej ogólny temat, jakiego poziomu kompatybilności należy oczekiwać między generatorami Url, takich jak UrlHelper.Action i wiążące parametr działania? Mam na myśli, czy to błąd, czy co? –

+0

Nie. Nie błąd. Nazwałbym to decyzją projektową. Przypadek, w którym masz wiele wartości dla parametru jest dość rzadki, szczególnie w przypadku get. Myślę, że oczekiwanie, że obiekt jest mapą prostych typów wartości, dobrze służy większości ludzi. – tvanfosson

+0

Dziękuję za to, że moje myślenie we właściwym kierunku. Nie będzie dokładnie działać, ponieważ łańcuch jest IEnumerable, który wyliczy znaki. Powoduje to także, że wszystkie parametry trasy są parami cyklicznymi, podczas gdy oryginalne łącze ActionLink uwzględnia trasy tworzone w pliku global.asax.cs w celu formatowania stylu REST parametrów trasy, które go obsługują. – puffpio

-2

nie jestem na mojej stacji roboczej, ale jak o czymś takim:

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

albo wpisane:

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

Nadal będzie wywoływać ToString() na obiekcie. – Omar

+0

ok ... cóż, próbowałem. sry – Chaddeus

4

można sufiks swojej routevalues z indeksu tablicy tak jak :

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

spowoduje to, że kwerenda zawierająca test=val1&test=val2 może pomóc?

+4

dziękuję, to jest genialne ... och, czekaj, nieważne, nie działało zgodnie z oczekiwaniami – JarrettV

+4

@JarrettV w prawo, to nie działa: test% 5B0% 5D = 1 i test% 5B1% 5D = 2 – Sasha

+0

@ JarrettV URL wydaje się być w porządku, jest tylko zakodowany (i powinien być) –

2

Łączenie obu metod działa ładnie.

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

Następnie użyj tej metody w dowolnej metodzie rozszerzenia, która wymaga parametrów routeValues ​​z IEnumerable (s).

Niestety, to obejście jest również potrzebne w MVC3.

0

To będzie działać tylko jako rozszerzenie do UrlHelper i wystarczy podać ładny adres URL, który będzie gotowy do umieszczenia w dowolnym miejscu, a nie cały tag, a także zachowa większość innych wartości trasy dla dowolnych innych używanych adresów URL. .. dając ci najbardziej przyjazny adres URL, który masz (minus wartości IEnumerable), a następnie po prostu dołącz wartości ciągu zapytania na końcu.

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

Jest librarly nazywa Unbinder, których można użyć, aby wstawić skomplikowanych obiektów do tras/URL.

To działa tak:

using Unbound; 

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