ASP.NET MVC - передать объект массива как значение маршрута в Html.ActionLink ()

У меня есть метод, который возвращает массив (string []), и я пытаюсь передать этот массив строк в Action Link, чтобы он создавал строку запроса, похожую на:

/Controller/Action?str=val1&str=val2&str=val3...etc

Но когда я передаю новый {str = GetStringArray ()}, я получаю следующий URL:

/Controller/Action?str=System.String%5B%5D

По сути, он берет мою строку [] и запускает на ней .ToString (), чтобы получить значение.

Любые идеи? Спасибо!


person Community    schedule 04.04.2009    source источник
comment
Вы когда-нибудь получали на это ответ?   -  person reach4thelasers    schedule 29.11.2009


Ответы (6)


Попробуйте создать RouteValueDictionary, содержащий ваши значения. Вам нужно будет дать каждой записи отдельный ключ.

<%  var rv = new RouteValueDictionary();
    var strings = GetStringArray();
    for (int i = 0; i < strings.Length; ++i)
    {
        rv["str[" + i + "]"] = strings[i];
    }
 %>

<%= Html.ActionLink( "Link", "Action", "Controller", rv, null ) %>

даст вам ссылку вроде

<a href='/Controller/Action?str=val0&str=val1&...'>Link</a>

ИЗМЕНИТЬ: MVC2 изменил интерфейс ValueProvider, чтобы мой исходный ответ стал устаревшим. Вы должны использовать модель с массивом строк в качестве свойства.

public class Model
{
    public string Str[] { get; set; }
}

Затем связыватель модели заполнит вашу модель значениями, которые вы передаете в URL-адресе.

public ActionResult Action( Model model )
{
    var str0 = model.Str[0];
}
person tvanfosson    schedule 04.04.2009
comment
Просто подумал, что упомянул, что похоже, что вы дали еще одну альтернативу аналогичному вопросу здесь: [ASP.Net MVC RouteData и массивы] (stackoverflow.com/questions/1752721/). Есть ли способ связать эти два вопроса, чтобы люди увидели обе ваши альтернативы? - person GuyIncognito; 20.01.2010
comment
Я думаю, ты только что это сделал. На самом деле это больше не сработает. Я обновлю метод действия, чтобы использовать модель. - person tvanfosson; 30.05.2010
comment
Проблема не в привязке модели. Кажется, что MVC 2 по-прежнему генерирует строки запроса, такие как ?str=System.String%5B%5D, когда значение RouteValueDictionary содержит массив / список / и т. Д. Все еще не обойтись? - person Crescent Fresh; 31.05.2010
comment
@Crescent - вы уверены, что используете подпись, которая имеет как значения маршрута, так и атрибуты html? - person tvanfosson; 31.05.2010
comment
@tvanfosson: Думаю, да. При передаче данных маршрута, таких как new { foo = new[] { "a", "b" } }, в любой из Html.ActionLink, Url.Action, HtmlHelper.GenerateLink и т. Д. Будет сгенерирован параметр строки запроса, например ?foo=System.String%5B%5D, а не ожидаемый ?foo=a&foo=b. Вы получите разные результаты, если укажете атрибуты html? - person Crescent Fresh; 31.05.2010
comment
@ Crescent - извините, я неправильно понял. Предлагаемое мной решение добавляет каждый из элементов массива индивидуально в RouteValueDictionary, избегая проблемы. Сериализатор выполняет итерацию по свойствам объекта, добавляя их как пары ключ / значение в словарь внутри. Очевидно, это не то, что вам нужно для массива. См. Мой другой ответ (цитируемый выше @Guy) для вспомогательного расширения, которое по-разному обрабатывает IEnumerables. Обратите внимание, что я не использую это - обычно я использую post и метод Фила Хаака для привязки списков объектов. - person tvanfosson; 31.05.2010
comment
Отлично, но не работает на dotnet Core. В ядре dotnet я получил экранированную строку. stackoverflow.com/questions/42461590/ - person Petr Tomášek; 26.02.2017

Это действительно раздражало меня, поэтому с помощью вдохновения Скотта Хансельмана я написал следующий (беглый) метод расширения :

public static RedirectToRouteResult WithRouteValue(
    this RedirectToRouteResult result, 
    string key, 
    object value)
{
    if (value == null)
        throw new ArgumentException("value cannot be null");

    result.RouteValues.Add(key, value);

    return result;
}

public static RedirectToRouteResult WithRouteValue<T>(
    this RedirectToRouteResult result, 
    string key, 
    IEnumerable<T> values)
{
    if (result.RouteValues.Keys.Any(k => k.StartsWith(key + "[")))
        throw new ArgumentException("Key already exists in collection");

    if (values == null)
        throw new ArgumentNullException("values cannot be null");

    var valuesList = values.ToList();

    for (int i = 0; i < valuesList.Count; i++)
    {
        result.RouteValues.Add(String.Format("{0}[{1}]", key, i), valuesList[i]);
    }

    return result;
}

Звоните так:

return this.RedirectToAction("Index", "Home")
           .WithRouteValue("id", 1)
           .WithRouteValue("list", new[] { 1, 2, 3 });
person dav_i    schedule 14.02.2014

Еще одно решение, которое пришло мне в голову:

string url = "/Controller/Action?iVal=5&str=" + string.Join("&str=", strArray); 

Это грязно, и вы должны протестировать его перед использованием, но, тем не менее, он должен работать. Надеюсь это поможет.

person Krisztián Balla    schedule 23.05.2014

Существует библиотека под названием Unbinder, которую вы можете использовать для вставки сложных объектов в маршруты / URL-адреса.

Работает это так:

using Unbound;

Unbinder u = new Unbinder();
string url = Url.RouteUrl("routeName", new RouteValueDictionary(u.Unbind(YourComplexObject)));
person Krisztián Balla    schedule 23.05.2014

Это массив HelperExtension, решающий проблемы с массивом и свойствами IEnumerable:

public static class AjaxHelperExtensions
{
    public static MvcHtmlString ActionLinkWithCollectionModel(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 System.Web.Mvc.Ajax.AjaxExtensions.ActionLink(ajaxHelper, linkText, actionName, rv, ajaxOptions, htmlAttributes);
    }
}
person GGO    schedule 19.09.2017

Я бы использовал POST для массива. Помимо уродства и злоупотребления GET, вы рискуете исчерпать пространство URL-адресов (хотите верьте, хотите нет).

Предполагая ограничение в 2000 байт. Накладные расходы строки запроса (& str =) сокращают вас до ~ 300 байт фактических данных (при условии, что остальная часть URL-адреса равна 0 байтам).

person Greg Dean    schedule 04.04.2009