Search code examples
c#asp.net-mvcarrayslistasp.net-mvc-routing

ASP.Net MVC RouteData and arrays


If I have an Action like this:

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

I can hit it with the following URL:

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

But in my ViewPage, I have this code:

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

Unfortunately, MVC is not smart enough to recognize that the action takes an array, and unrolls the list to form the proper url route. instead it just does a .ToString() on the object which just lists the data type in the case of a List.

Is there a way to get Html.ActionLink to generate a proper URL when one of the destination Action's parameters is an array or list?

-- edit --

As Josh pointed out below, ViewData["stuff"] is just an object. I tried to simplify the problem but instead caused an unrelated bug! I'm actually using a dedicated ViewPage<T> so I have a tightly coupled type aware Model. The ActionLink actually looks like:

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

Where ViewData.Model.Stuff is typed as a List


Solution

  • I'm thinking that a custom HtmlHelper would be in order.

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