Как работать с RouteValues с несколькими значениями с одинаковым именем
В моем приложении ASP.NET MVC 4 я могу фильтровать по нескольким тегам. В HTML это выглядит так:
<form>
<label>
<input type="checkbox" name="tag" value="1">One
</label>
<label>
<input type="checkbox" name="tag" value="2">Two
</label>
<label>
<input type="checkbox" name="tag" value="3">Three
</label>
<input type="submit" name="action" value="Filter">
</form>
При установке первого и третьего флажка строка запроса сериализуется как ?tag=1&tag=3
и мой контроллер красиво передает объект с типом следующего класса:
// Filter class
public class Filter {
public ICollection<int> tag { get; set; }
}
// Controller method
public ActionResult Index(AdFilter filter)
{
string url = Url.Action("DoFilter", filter);
// url gets this value:
// "/controller/Index?tag=System.Collections.Generic.List%601%5BSystem.Int32%5D"
// I would expect this:
// "/controller/Index?tag=1&tag=3"
...
}
Тем не менее, призыв к Url.Action
приводит к сериализации типового имени коллекции вместо фактических значений.
Как это может быть сделано?
Стандартная инфраструктура MVC может обрабатывать мульти-ключи, описанные как ввод. Разве нет стандартной инфраструктуры, которая могла бы справиться с этим наоборот? Я что-то пропустил?
4 ответа
Вы можете сделать это следующим образом:
string url = Url.Action("DoFilter", TypeHelper2.ObjectToDictionary(filter) );
TypeHelper2.ObjectToDictionary
является модифицированной версией внутреннего метода.NET и может быть найден в этом двух файлах.
Изменено поведение: когда предмет реализует IEnumerable
, для каждого элемента в возвращаемом словаре создается запись с ключом as "Name[index]"
(индекс основан на 0). Это возможно, потому что связыватель контроллера MVC может обрабатывать как tag=1&tag=3
а также tag[0]=1&tag[1]=3
Строки запроса.
Простое, но не столь элегантное решение может быть:
public ActionResult Index(AdFilter filter)
{
string parameters = "?";
foreach (var item in filter.tag)
{
parameters += string.Format("tag={0}&", item);
}
//trimming the last "&"
parameters = parameters.TrimEnd(parameters[parameters.Length - 1]);
string url = Url.Action("DoFilter") + parameters;
}
Проголосовали за вышеупомянутые 2 ответа - они оба совершенно хорошие ответы. С этой стороны (перенаправление на действие или создание URL-адреса для перенаправления) MVC вообще не "любит" массивы.
Есть два дальнейших подхода, которые я вижу:
1) использовать TempData для сохранения извлечения массива (например, в нижней части этой статьи)
2) Напишите и используйте пользовательское связующее для AdFilter
Я бы сам выбрал последнее - тестируемое и более детерминированное (также я не знаю, как работает TempData, когда вы входите в сценарий фермы серверов)
Еще одна вещь, которую вы можете рассмотреть, - это использовать что-то вроде возврата от действия Index.
return View("DoFilter", new AdFilter(){tag = tag});
(Это вернет представление Dofilter при сохранении URL-адреса "index? Tag [0] = 1 & tag 1= 2" в браузере)
Наконец, создается впечатление, что вы используете критерии фильтра только для того, чтобы отправить их обратно в браузер, чтобы браузер мог затем снова запросить результаты фильтра. Возможно, лучшим вариантом было бы установить действие в форме, чтобы публиковать в "DoFilter" с самого начала:
<form method="post" action="DoFilter">
НТН
Вы используете перегрузку Url.Action()
который принимает объект для генерации параметров маршрута. Внутренне это работает с помощью отражения, чтобы построить словарь каждого имени свойства и его .ToString()
значение.
В случае простого типа значения это может быть Name = "doekman"
(который становится /DoFilter?Name=doekman
), но в случае свойства, которое является сложным объектом или коллекцией, оно возвращает имя типа (то есть .ToString()
значение). Никакой рекурсии не делается, и это имеет смысл, потому что строки запроса имеют ограничения по длине, поэтому, если бы рекурсия выполнялась для сложных объектов и коллекций, вы вскоре превысили бы этот предел и выдавали исключение, если в коллекции содержалось много элементов (и это создавало бы действительно безобразная строка запроса).
Так что в ответ на How can this be done?
Вы не можете, если вы не создаете вручную RouteValueDictionary
(или напишите свой собственный помощник для генерации строки запроса)