Asp.Net mvc 5 - Как передать сложный объект в качестве значения маршрута в Html.ActionLink(), поэтому привязка модели по умолчанию может отобразить его?

У меня есть объект, содержащий параметры поиска, сортировки и подкачки, а также идентификатор редактируемой записи.

Я хотел бы передать этот объект в Html.ActionLink() как объект значения маршрута, так что полученная строка запроса будет правильно отображена связующим устройством по умолчанию в параметр действия редактирования, который является EditViewModel.

Идея состоит в том, что после завершения действия "Редактировать" она может перенаправлять обратно в индекс и поддерживать ту же позицию подкачки/сортировки в том же наборе данных и фильтроваться одной и той же строкой поиска.

Изменить модель просмотра:

public class EditViewModel
{
    public SearchSortPageViewModel SearchSortPageParams { get; set; }
    public int Id { get; set; }

    public EditViewModel() 
    {
        SearchSortPageParams = new SearchSortPageViewModel();
        Id = 0;
    }

    public EditViewModel(SearchSortPageViewModel searchSortPageParams, int id)
    {
        SearchSortPageParams = searchSortPageParams;
        Id = id;
    }
}

public class SearchSortPageViewModel
{
    public string SearchString { get; set; }
    public string SortCol { get; set; }
    public string SortOrder { get; set; }
    public int Page { get; set; }
    public int PageSize { get; set; }
}

Изменить действие:

public ActionResult Edit(EditViewModel evm)
    {
        /* ... */
    }

Когда я делаю это в представлении:

@model MyApp.Areas.Books.ViewModels.Books.IndexViewModel
...
@{EditViewModel evm = new EditViewModel(Model.SearchSortPageParams, item.ID);}
@Html.ActionLink("Edit", "Edit", evm)

Я получаю это:

http://localhost:63816/Books/Books/Edit/4?SearchSortPageParams=MyApp.Areas.Base.ViewModels.SearchSortPageViewModel

Но я хочу это:

http://localhost:63816/Books/Books/Edit/4?SearchSortPageParams.SearchString=abc&SearchSortPageParams.SortCol=name&SearchSortPageParams.SortOrder=asc&SearchSortPageParams.Page=1&SearchSortPageParams.PageSize=3

Единственный способ, которым я смог передать объект до сих пор, заключался в том, чтобы вручную подготовить строку запроса, например:

@{string theQueryString = "?SearchSortPageParams.SearchString=" + @evm.SearchSortPageParams.SearchString + "&SearchSortPageParams.SortCol=" + @evm.SearchSortPageParams.SortCol + "&SearchSortPageParams.SortOrder=" + @evm.SearchSortPageParams.SortOrder + "&SearchSortPageParams.Page=" + @evm.SearchSortPageParams.Page + "&SearchSortPageParams.PageSize=" + @evm.SearchSortPageParams.PageSize;}
<a href="@Url.Action("Edit", new { evm.Id })@(theQueryString)">Edit</a>

Я думал о написании настраиваемого связующего объекта, но кажется глупым, учитывая, что связующее устройство по умолчанию уже обрабатывает вложенные объекты, если отформатирован как строка запроса так, как он ожидает.

Я также подумал о написании настраиваемого сериализатора объектов, который выводит последовательный формат, который ожидает связующее устройство по умолчанию, но еще не пошел по этому маршруту.

Наконец, я подумал о том, чтобы сгладить EditViewModel, поэтому нет ничего вложенного, просто нужно, чтобы все свойства отображались на месте. Но это не идеально.

Итак, каков наилучший способ?

Ответ 1

Насколько я знаю, вы не можете передать сложный объект напрямую, но вы можете избежать необходимости сами строить строку запроса, передав RouteValueDictionary:

@Html.ActionLink("Edit", "Edit", new RouteValueDictionary {
    {"SearchSortPageParams.SortOrder", evm.SearchSortPageParams.SortOrder },
    { /* etc... */ }
})

Это должно генерировать строку запроса по мере необходимости.

Единственной альтернативой было бы использование отражения для итерации по свойствам модели и генерации этого словаря таким образом, но это, на мой взгляд, было бы чрезмерно спроектировано.

Конечно, я бы вообще предложил в этой ситуации, что вы просто используете свой метод действий для отдельных параметров:

public ActionResult Search(string searchString, SortOrder sortOrder, ...)

Я бы счел это более подходящим способом передать параметры GET методу (конечно, это может стать громоздким, если у вас много параметров). Тогда вы можете просто сделать следующее, что намного более аккуратно:

@Html.ActionLink("Edit", "Edit",
    new { sortOrder = evm.SearchSortPageParams.SortOrder, ... })