Как выбрать элемент из тега <select>и сохранить его как внешний ключ в ASP .NET Core 2.0 - PullRequest
0 голосов
/ 10 мая 2018

У меня есть эти два класса: State и Station:

 public class State
{
    public int Id { get; set; }

    [Required]
    public string Code { get; set; }

    [Required]
    public string Name { get; set; }

    public virtual ICollection<Station> Stations { get; set; }
}

и

public class Station
{
    public int Id { get; set; }

    [Required]
    public string Code { get; set; }

    [Required]
    public string Name { get; set; }

    [Required]
    public virtual State State { get; set; }
}

Дизайн был «Code First», и я использовал миграцию для настройки базы данных, и это было похоже на

Таблица станции сохраняет StateId как внешний ключ, как и должно быть

В моем файле StationsController.cs есть метод Create, в котором я использую ViewBag для перечисления названий состояний следующим образом:

// GET: Stations/Create
    public IActionResult Create()
    {
        ViewBag.StatesList = _context.States.ToList();
        return View();
    }

И, наконец, мой HttpPost Create метод

[HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create([Bind("Id,Code,Name,State")] Station station)
    {
        if (ModelState.IsValid)
        {
            _context.Add(station);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        return View(station);
    }

Мой create.cshtml файл имеет <select> тег, подобный этому:

 <div class="form-group">
            <label asp-for="State" class="control-label"></label>
            <select asp-for="State" asp-items="@(new SelectList(ViewBag.StatesList, "Id", "Name","State"))" ></select>
            <span asp-validation-for="State" class="text-danger"></span>
        </div>

Проблема, с которой я сталкиваюсь, заключается в том, что после нажатия submit мой ModelState.isValid остается false, а поле State равно null, как показано на этом рисунке:

Поле состояния равно нулю

Controller был сгенерирован автоматически, и я изменил только две вещи: во-первых, я добавил ViewBag.StateList в методе Create(), а во-вторых, я добавил поле State в Create([Bind("Id,Code,Name,State")] .

Любая помощь будет принята с благодарностью и извините за длинный пост ..

С уважением Ashutosh

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

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

Вот что я сделаю (все написал от руки, не проверял).

Создание модели представления для представления создания станции:

public class CreateStationViewModel
{
    // You shouldn't have Station ID here as it's creation

    [Required]
    public string Code { get; set; }

    [Required]
    public string Name { get; set; }

    [Display(Name = "State")]
    public int SelectedStateId { get; set; }

    public IDictionary<int, string> AvailableStates { get; set; }
}

Инициализируйте эту модель представления по методу get:

public IActionResult Create()
{
    var vm = new CreateStationViewModel
    {
        // Construct a list of available states.
        // We will use it as the dropdown options.
        AvailableStates = _context.States
            .ToDictionary(x => x.Id, x => $"{ x.Name }({ x.Code })")
    };

    return View(vm);
}

Построить форму на виде:

@model CreateStationViewModel
@{
    // You can define a variable here for the dictionary-to-selectListItem
    // conversion.
    // Or you can write an extension method on IDictionary for that purpose.
    var availableStatesSelectListItems = Model.AvailableStates
        .Select(x => new SelectListItem
        {
            Text = x.Value.ToString(),
            Value = x.Key.ToString()
        });
}

<form asp-area="" asp-controller="station" asp-action="create">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>

    <div class="form-group">
        <label asp-for="Code" class="control-label"></label>
        <input type="text" class="form-control" asp-for="Code" />
        <span class="form-text" asp-validation-for="Code"></span>
    </div>
    <div class="form-group">
        <label asp-for="Name" class="control-label"></label>
        <input type="text" class="form-control" asp-for="Name" />
        <span class="form-text" asp-validation-for="Name"></span>
    </div>
    <div class="form-group">
        <label asp-for="SelectedStateId" class="control-label"></label>
        <select class="form-control" asp-for="SelectedStateId"
            asp-items="availableStatesSelectListItems">
            <option value="">- select -</option>
        </select>
        <span class="form-text" asp-validation-for="SelectedStateId"></span>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-success">Create</button>
    </div>
</form>

Прослушивание просмотра модели при обратной передаче:

[HttpPost]
public IActionResult Create(CreateStationViewModel model)
{
    if (ModelState.IsValid)
    {
        // You build the entity model from the view model
        _context.Stations.Add(new Station
        {
            Code = model.Code,
            Name = model.Name,
            StateId = model.SelectedStateId
        });
        _context.SaveChanges();

       return RedirectToAction("index");
    }

    // Rebuild the available states, or 
    // better, you can use ajax for the whole form (different topic)
    model.AvailableStates = _context.States
            .ToDictionary(x => x.Id, x => $"{ x.Name }({ x.Code })");

    return View(model);
}
0 голосов
/ 10 мая 2018

AFAIK, вы не можете передать объект State для использования в качестве значения элементов <option> внутри вашего <select>. Вместо этого вы должны использовать более простой тип данных. На практике вам нужно использовать StateId в качестве значения.

Добавьте свойство StateId к вашему Station классу:

public class Station
{
    public int Id { get; set; }

    [Required]
    public string Code { get; set; }

    [Required]
    public string Name { get; set; }

    public virtual State State { get; set; }

    [Required]
    public int StateId { get; set; }
}

Кроме того, ваш конструктор SelectList неверен. Четвертый аргумент («State» в вашем примере) должен определять выбранный элемент по умолчанию. Вам не нужно определять это здесь, поэтому не указывайте:

<select asp-for="StateId" asp-items="@(new SelectList(ViewBag.StatesList, "Id", "Name"))" ></select>

Теперь ваш элемент <select> должен иметь возможность выбрать правильный Id и заполнить поле StateId. Обратите внимание, что в методе [HttpPost], State будет по-прежнему нулевым, но ваша база данных должна быть обновлена ​​с правильным StateId.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...