Как работать с 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 (или напишите свой собственный помощник для генерации строки запроса)

Другие вопросы по тегам