在 ActionLink 的 RouteValues 中传递 IEnumerable 属性
Passing IEnumerable property in RouteValues of ActionLink
想象一个定义如下的对象:
public class MyViewModel{
public List<string> MyList { get; set; }
}
在我看来,我有此操作 link :
@Ajax.ActionLink("<", "Index", new MyViewModel() { MyList = new List<string>() {"foo", "bar"}}, new AjaxOptions())
ActionLink 的 html 结果将是:
<a class="btn btn-default" data-ajax="true" href="/Index?MyList=System.Collections.Generic.List%601%5BSystem.String%5D"><</a>
我的问题是,如何得到这个结果:
<a class="btn btn-default" data-ajax="true" href="/Index?MyList=foo&MyList=bar"><</a>
您不能使用 @Html.ActionLink()
为集合生成路由值。在内部方法(以及所有生成 url 的 MVC 方法)使用 属性 的 .ToString()
方法来生成 route/query 字符串值(因此你的 MyList=System.Collections.Generic.List%601%5BSystem.String%5D"
结果)。
该方法不对复杂属性或集合执行递归是有充分理由的 - 除了丑陋的查询字符串外,您很容易超出查询字符串限制并引发异常。
不清楚为什么要这样做(正常的方式是传递对象的ID,然后根据ID在GET方法中再次获取数据),但是你可以这样做创建一个带有索引 属性 名称的 RouteValueDictionary
,并在您的 @Ajax.ActionLink()
方法中使用它。
在视图中
@{
var rvd = new RouteValueDictionary();
rvd.Add("MyList[0]", "foo");
rvd.Add("MyList[1]", "bar");
}
@Ajax.ActionLink("<", "Index", rvd, new AjaxOptions())
这将使一个 GET 到
public ActionResult Index(MyViewModel model)
但是您还必须将 MyList
设为 属性(DefaultModelBinder
不绑定字段)
public class MyViewModel{
public List<string> MyList { get; set; } // add getter/setter
}
然后 POST 方法中 model.MyList
的值将是 ["foo", "bar"]
。
通过 Stephen 的回答,我开发了一个辅助扩展方法来执行此操作。
注意 URL 查询字符串限制 :如果集合中的值太多, URL 可以大于 255 个字符并抛出一个例外。
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);
}
}
你可以试试string.Join。像这样
@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
)
想象一个定义如下的对象:
public class MyViewModel{
public List<string> MyList { get; set; }
}
在我看来,我有此操作 link :
@Ajax.ActionLink("<", "Index", new MyViewModel() { MyList = new List<string>() {"foo", "bar"}}, new AjaxOptions())
ActionLink 的 html 结果将是:
<a class="btn btn-default" data-ajax="true" href="/Index?MyList=System.Collections.Generic.List%601%5BSystem.String%5D"><</a>
我的问题是,如何得到这个结果:
<a class="btn btn-default" data-ajax="true" href="/Index?MyList=foo&MyList=bar"><</a>
您不能使用 @Html.ActionLink()
为集合生成路由值。在内部方法(以及所有生成 url 的 MVC 方法)使用 属性 的 .ToString()
方法来生成 route/query 字符串值(因此你的 MyList=System.Collections.Generic.List%601%5BSystem.String%5D"
结果)。
该方法不对复杂属性或集合执行递归是有充分理由的 - 除了丑陋的查询字符串外,您很容易超出查询字符串限制并引发异常。
不清楚为什么要这样做(正常的方式是传递对象的ID,然后根据ID在GET方法中再次获取数据),但是你可以这样做创建一个带有索引 属性 名称的 RouteValueDictionary
,并在您的 @Ajax.ActionLink()
方法中使用它。
在视图中
@{
var rvd = new RouteValueDictionary();
rvd.Add("MyList[0]", "foo");
rvd.Add("MyList[1]", "bar");
}
@Ajax.ActionLink("<", "Index", rvd, new AjaxOptions())
这将使一个 GET 到
public ActionResult Index(MyViewModel model)
但是您还必须将 MyList
设为 属性(DefaultModelBinder
不绑定字段)
public class MyViewModel{
public List<string> MyList { get; set; } // add getter/setter
}
然后 POST 方法中 model.MyList
的值将是 ["foo", "bar"]
。
通过 Stephen 的回答,我开发了一个辅助扩展方法来执行此操作。
注意 URL 查询字符串限制 :如果集合中的值太多, URL 可以大于 255 个字符并抛出一个例外。
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);
}
}
你可以试试string.Join。像这样
@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
)