问题
Imagine an object defined like :
public class MyViewModel{
public List<string> MyList { get; set; }
}
In my view, i have this Action link :
@Ajax.ActionLink("<", "Index", new MyViewModel() { MyList = new List<string>() {"foo", "bar"}}, new AjaxOptions())
The html result of the ActionLink will be :
<a class="btn btn-default" data-ajax="true" href="/Index?MyList=System.Collections.Generic.List%601%5BSystem.String%5D"><</a>
My question is, how get this result rather :
<a class="btn btn-default" data-ajax="true" href="/Index?MyList=foo&MyList=bar"><</a>
回答1:
You cannot use @Html.ActionLink()
to generate route values for a collection. Internally the method (and all the MVC methods that generate urls) uses the .ToString()
method of the property to generate the route/query string value (hence your MyList=System.Collections.Generic.List%601%5BSystem.String%5D"
result).
The method does not perform recursion on complex properties or collections for good reason - apart from the ugly query string, you could easily exceed the query string limit and throw an exception.
Its not clear why you want to do this (the normal way is to pass an the ID of the object, and then get the data again in the GET method based on the ID), but you can so this by creating a RouteValueDictionary
with indexed property names, and use it in your@Ajax.ActionLink()
method.
In the view
@{
var rvd = new RouteValueDictionary();
rvd.Add("MyList[0]", "foo");
rvd.Add("MyList[1]", "bar");
}
@Ajax.ActionLink("<", "Index", rvd, new AjaxOptions())
Which will make a GET to
public ActionResult Index(MyViewModel model)
However you must also make MyList
a property (the DefaultModelBinder
does not bind fields)
public class MyViewModel{
public List<string> MyList { get; set; } // add getter/setter
}
and then the value of model.MyList
in the POST method will be ["foo", "bar"]
.
回答2:
With Stephen's anwser, i have develop a helper extension method to do this.
Be careful of the URL query string limit : if the collection has too many values, the URL can be greater than 255 characters and throw an exception.
public static class AjaxHelperExtensions
{
public static MvcHtmlString ActionLinkUsingCollection(this AjaxHelper ajaxHelper, string linkText, string actionName, object model, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes)
{
var rv = new RouteValueDictionary();
foreach (var property in model.GetType().GetProperties())
{
if (typeof(ICollection).IsAssignableFrom(property.PropertyType))
{
var s = ((IEnumerable<object>)property.GetValue(model));
if (s != null && s.Any())
{
var values = s.Select(p => p.ToString()).Where(p => !string.IsNullOrEmpty(p)).ToList();
for (var i = 0; i < values.Count(); i++)
rv.Add(string.Concat(property.Name, "[", i, "]"), values[i]);
}
}
else
{
var value = property.GetGetMethod().Invoke(model, null) == null ? "" : property.GetGetMethod().Invoke(model, null).ToString();
if (!string.IsNullOrEmpty(value))
rv.Add(property.Name, value);
}
}
return AjaxExtensions.ActionLink(ajaxHelper, linkText, actionName, rv, ajaxOptions, htmlAttributes);
}
}
回答3:
You can try string.Join. Something like this
@Ajax.ActionLink(
"Your text", -- <
"ActionName", -- Index
new
{
MyList =string.Join(",", new List<string>() {"foo", "bar"}),
otherPropertiesIfyouwant = YourValue
}, -- rounteValues
new AjaxOptions { UpdateTargetId = "..." }, -- Your Ajax option --optional
new { @id = "back" } -- Your html attribute - optional
)
来源:https://stackoverflow.com/questions/46304796/passing-ienumerable-property-in-routevalues-of-actionlink