Данные формы не привязываются к модели представления с универсальным типом

У меня есть приложение, написанное с использованием C# поверх ASP.NET Core 5.0.

У меня есть следующая модель представления

public class TestVM 
{
   public Name  { get; set; }

   public MenuViewModel<string> State { get; set;}

   public TestVM()
   {
       State = MenuViewModel<string>();
   }
}

Вот урезанная версия моего MenuViewModel


public class MenuViewModel
{
    [BindNever]
    public IEnumerable<SelectListItem> Items { get; set; }
}

public class MenuViewModel<T> : MenuViewModel
{
    public T Value { get; set; }
}

Проблема в том, что когда приходит почтовый запрос, viewModel.State.Value имеет значение null. Когда я оцениваю Request.Form, я вижу ключ State.Value с правильным значением CA

Вот урезанный мой метод действия в контроллере.

[HttpPost, ValidateAntiForgeryToken]
public IActionResult Store(TestVM viewModel)
{

   if(ModelState.IsValid)
   {
     // do some
   }

   return View(viewModel);
}

Как правильно привязать данные формы из запроса к свойству State.Value?

Обновлено. Я создал шаблон редактора, позволяющий отображать MenuVieModel. ~/Views/Shared/EditorTemplates/MenuViewModel.cshtml содержит следующий код

@model dynamic

@{
    if (!(Model is MenuViewModel m))
    {
        return;
    }

    dynamic obj = new System.Dynamic.ExpandoObject();
    obj.Class = "form-control";
    if (Html.ViewData.ModelMetadata.IsRequired)
    {
        obj.Required = true;
    }
}

@Html.DropDownList("Value", m.Options, Html.ViewData.ModelMetadata.Placeholder, obj)

person Jay    schedule 07.12.2020    source источник
comment
Показать соответствующую часть представления.   -  person mxmissile    schedule 08.12.2020
comment
@mxmissile, пожалуйста, ознакомьтесь с обновленным постом   -  person Jay    schedule 08.12.2020
comment
Как вы визуализируете MenuViewModel.cshtml? Не могли бы вы поделиться методом получения? И каков ваш m.Options в представлении бритвы? Когда я копирую ваш код, он выдает ошибку: 'MenuViewModel' does not contain a definition for 'Options' and no accessible extension method 'Options' accepting a first argument of type 'MenuViewModel' could be found (are you missing a using directive or an assembly reference?). Содержит ли ваша MenuViewModel свойство Options?   -  person Rena    schedule 08.12.2020


Ответы (1)


Во-первых, вам нужно знать, что для каждого свойства сложного типа привязка модели просматривает источники для шаблона имени prefix.property_name. Если ничего не найдено, ищется просто property_name без префикса.

Вот рабочая демонстрация, которой вы можете следовать:

Модель:

public class TestVM
{
    public string Name { get; set; }

    public MenuViewModel<string> State { get; set; }
    public TestVM()
    {
        State =new MenuViewModel<string>();
    }
}
public class MenuViewModel
{
    [BindNever]
    public IEnumerable<SelectListItem> Items { get; set; }
}

public class MenuViewModel<T> : MenuViewModel
{
    public T Value { get; set; }
}

Вид:

@model dynamic

@{
    if (!(Model is MenuViewModel m))
    {
        return;
    }

    dynamic obj = new System.Dynamic.ExpandoObject();
    obj.Class = "form-control";
    if (Html.ViewData.ModelMetadata.IsRequired)
    {
        obj.Required = true;
    }
}
<form asp-action="Store">

          @*change here,And I do not find Options in your MenuViewModel,So I change it to Items*@
    @Html.DropDownList("State.Value", m.Items, Html.ViewData.ModelMetadata.Placeholder, obj)
    <input type="submit" value="post" />
</form>

Контроллер:

public IActionResult Index()
{
    var model = new MenuViewModel<string>()
    {
        Items = new List<SelectListItem>() {
            new SelectListItem() { Value = "-1", Text = "--- Select ---" },
            new SelectListItem() { Value = "org1", Text = "org1" },
            new SelectListItem() { Value = "org2", Text = "org2" },
            new SelectListItem() { Value = "org3", Text = "org3" }
            }
    };        
    return View(model);
}
[HttpPost, ValidateAntiForgeryToken]
public IActionResult Store(TestVM viewModel)
{
    if (ModelState.IsValid)
    {
        // do some
    }

    return View(viewModel);
}

Результат: введите здесь описание изображения

person Rena    schedule 08.12.2020
comment
Хм, значит, мой код у тебя работает? Мне интересно, почему Value для меня всегда равно null, даже если в запросе есть State.Value. - person Jay; 08.12.2020
comment
Нет, ваш код у меня не работает. И я думаю, вам нужно поделиться со мной всем кодом, я не смог найти m.Options в вашем представлении бритвы. Пожалуйста, проверьте мое представление бритвы, мой код не совпадает с вашим. - person Rena; 08.12.2020
comment
Да. Извините, это была ошибка копирования/вставки. Правильный код — m.Items. Но привязка модели работает для вас, а не для меня. Для меня State.Value всегда равно null - person Jay; 08.12.2020
comment
Вы изменили @Html.DropDownList("Value" на @Html.DropDownList("State.Value"? Кроме того, проверьте мою модель, модель, которую вы предоставили, также неверна. Проверьте конструктор и имя в TestVM. - person Rena; 08.12.2020
comment
Спасибо за помощь! Проблема заключалась в том, что в моем коде у меня было public T Value вместо public T Value { get; set; }, поэтому для Value всегда было установлено значение null. Но чтобы представление работало, следует использовать Value, а не State.Value. - person Jay; 08.12.2020
comment
Ваше решение немного невероятно, потому что в вашем вопросе значение содержит геттер-сеттер. И если вы получаете TestVM в действии, то, что вы публикуете, должно быть состоянием. Значение, это дизайн в системе привязки модели. Возможно, то, что вы указали в своем вопросе все равно чего-то не хватает. В будущем я предлагаю вам поделиться правильным кодом, который может воспроизвести проблему, иначе мы не сможем вам помочь. - person Rena; 09.12.2020