在ActionLink的RouteValues中传递IEnumerable属性

时间:2017-09-19 15:49:37

标签: c# razor asp.net-mvc-5 html-helper

想象一个定义如下的对象:

public class MyViewModel{
    public List<string> MyList { get; set; }
}

在我看来,我有这个动作链接:

@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">&lt;</a>

我的问题是,如何得到这个结果:

<a class="btn btn-default" data-ajax="true" href="/Index?MyList=foo&MyList=bar">&lt;</a>

3 个答案:

答案 0 :(得分:1)

您无法使用@Html.ActionLink()为集合生成路由值。在内部,方法(以及生成URL的所有MVC方法)使用属性的.ToString()方法生成路由/查询字符串值(因此您的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"]

答案 1 :(得分:1)

对于Stephen的anwser,我已经开发了一个辅助扩展方法来做到这一点。

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

答案 2 :(得分:1)

您可以尝试 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
            )