abenci
abenci

Reputation: 8651

Passing an array to Html.ActionLink()

I followed this example:

ASP.NET MVC - Pass array object as a route value within Html.ActionLink(...)

But, my Action is always called with null. What am I doing wrong?

foreach (OrderDetail od in order.OrderDetails)
{
   rvd.Add("key" + count++, productID);
   rvd.Add("key" + count++, productName);
}

@Html.ActionLink(linkText, "Renew", "Orders", rvd, new Dictionary<string, object>())

The query string is correctly generated, like ?key0=dog&key1=cat&key2=fish..., but I get a null parameter in my Action below:

public ActionResult Renew(RouteValueDictionary rvd)
{
   // 'rvd' is null here!
}

Please note: I don't know the number of parameters in advance.

Upvotes: 1

Views: 4790

Answers (2)

hawkke
hawkke

Reputation: 4262

I imagine what is happening is you are expecting the model binder to bind your array to a RouteValueDictionary, but the model binder doesn't know that key0=dog&key1=cat&key2=fish is supposed to be a dictionary. I would recommend changing your code to accept a string array. To do this, your query string needs to look something like this: ?rvd=dog&rvd=cat&rvd=fish

And your Action...

public ActionResult Renew(string[] rvd)
{
   // 'rvd' is no longer null here!
}

The important part is rvd is the parameter name in your action, as well as the name of each element in the querystring: ?rvd=dog&rvd=cat&rvd=fish. If you really want to use a dictionary instead of a string array, then your querystring should look like this: ?rvd[0]=dog&rvd[1]=cat&rvd[2]=fish, giving each item an array index, but you would probably have to change your parameter from RouteValueDictionary to Dictionary<string,string>, I'm not quite sure. More info here. EDIT: See Darin's comment about binding to a dictionary, as I believe his is correct.

You may have to write your own extension for Html.ActionLink that accepts an array (or whatever OrderDetails is) and creates the querystring as an array. This looks like a pretty good starting place.

Upvotes: 1

Darin Dimitrov
Darin Dimitrov

Reputation: 1038930

The query string is correctly generated, like ?key0=dog&key1=cat&key2=fish...

No, this is not a correct url. A correct url would have looked like this:

?%5B0%5D.Key=123&%5B0%5D.Value=dog&%5B1%5D.Key=456&%5B1%5D.Value=cat...

which would have mapped to:

public ActionResult Renew(Dictionary<int, string> rvd)
{
    ...
}

You could write a custom ActionLink to generate this url:

public static class LinkExtensions
{
    public static IHtmlString MyActionLink(
        this HtmlHelper html,
        string linkText,
        string actionName,
        string controllerName,
        IDictionary<string, string> parameters
    )
    {
        var a = new TagBuilder("a");
        var urlHelper = new UrlHelper(html.ViewContext.RequestContext);
        var query = string.Join("&", parameters.Select((x, i) => string.Format("[{0}].Key={1}&[{0}].Value={2}", i, urlHelper.Encode(x.Key), urlHelper.Encode(x.Value))));
        var url = string.Format(
            "{0}?{1}",
            urlHelper.Action(actionName, controllerName, null, html.ViewContext.HttpContext.Request.Url.Scheme),
            query
        );
        a.Attributes["href"] = url;
        a.SetInnerText(linkText);
        return new HtmlString(a.ToString());
    }
}

which you could use like this in your view:

@Html.MyActionLink(
    linkText, 
    "Renew", 
    "Orders", 
    order.OrderDetails.ToDictionary(x => x.ProductID.ToString(), x => x.ProductName)
)

You can read more about the correct wire format for binding to various collections in this blog post.

Upvotes: 4

Related Questions