将数据从 ajax 请求传递到控制器操作时存在问题。
这是我的模型:
public class Mark
{
public int Id { get; set; }
[Required]
[Display(Name = "Марка")]
public string MarkName { get; set; }
public virtual ICollection<CarModel> CarModels { get; set; }
public virtual ICollection<Car> Cars { get; set; }
public Mark()
{
CarModels = new List<CarModel>();
Cars = new List<Car>();
}
}
public class CarModel
{
public int Id { get; set; }
[Required]
[Display(Name = "Модель")]
public string ModelName { get; set; }
public int MarkId { get; set; }
public virtual Mark Mark { get; set; }
public virtual ICollection<Equipment> Equipments { get; set; }
public virtual ICollection<Car> Cars { get; set; }
public CarModel()
{
Equipments = new List<Equipment>();
Cars = new List<Car>();
}
}
以下是我的部分观点:
_AddMarkModel:
@model AutoStore.Domain.Core.CarModel
<div id="AddMarkModel" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">Добавление новой марки/модели</h4>
</div>
@using (Ajax.BeginForm(new AjaxOptions { OnSuccess = "AddMarkSuccess", OnFailure = "AddMarkError" }))
{
<div class="modal-body">
@Html.Partial("_MarkModel")
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Закрыть</button>
<button id="btnConfirm" type="submit" class="btn btn-success">Добавить</button>
</div>
}
</div>
</div>
</div>
_MarkModel:
@model AutoStore.Domain.Core.CarModel
<div class="container">
<div class="row">
<div class="col">
@Html.LabelFor(i => i.Mark.MarkName, "Марка")
</div>
<div class="col-sm-3">
<div class="row">
<div class="col">
<div class="form-group">
@Html.RadioButton("Mark", "New", true)
<span>Новая</span>
@Html.EditorFor(i => i.Mark.MarkName, new { htmlAttributes = new { @id = "newMark", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Mark.MarkName, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.ModelName, "Модель")
@Html.EditorFor(i => i.ModelName, new { htmlAttributes = new { @id = "txtPass", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.ModelName, "", new { @class = "text-danger" })
</div>
</div>
</div>
</div>
<div class="col-sm-3">
<div class="row">
<div class="col">
@Html.RadioButton("Mark", "Existing")
<span>Существующая</span>
@Html.DropDownListFor(i => i.Mark.Id, ViewBag.marks as SelectList, new { @id = "exMark", @class = "form-control", @disabled = "disabled" })
@Html.ValidationMessageFor(i => i.Mark.MarkName, "", new { @class = "text-danger" })
</div>
</div>
</div>
</div>
</div>
从他们可以看出我正在使用 Html.AjaxBegin。
这导致了行动:
[HttpPost]
public JsonResult _AddMarkModel(CarModel model)
{
if (model == null)
throw new Exception("Модель не найдена");
if (ModelState.IsValid)
{
unitOfWOrk.CarModels.Create(model);
unitOfWOrk.Save();
return Json(new { result = true });
}
else
throw new Exception("Не все обязательные поля заполнены");
}
但是当我填写动作中的字段时,只有汽车型号的数据到达,而汽车品牌的数据没有到达:
虽然我有另一个使用 Car 类的表单,但 ajax.beginform 以相同的方式将数据传递给控制器。在外键的帮助下,所有其他模型的所有数据都被提取出来。这是汽车类:
public class Car
{
public int Id { get; set; }
[Required]
[Display(Name = "Цена")]
public int Price { get; set; }
[Required]
[Display(Name = "Количество на складе")]
public int Count { get; set; }
public int? MarkId { get; set; }
public virtual Mark Mark { get; set; }
public int CarModelId { get; set; }
public virtual CarModel CarModel { get; set; }
public int? EquipmentId { get; set; }
public virtual Equipment Equipment { get; set; }
public virtual ICollection<Order> Orders { get; set; }
public Car()
{
Orders = new List<Order>();
}
}
我怎么不明白有什么区别?请告诉我是否有人看到问题所在。也许问题出在数据库的体系结构中,在 car 的情况下实体以某种方式猜测将哪些数据放在哪里,但在 carmodel 的情况下却没有,但我无法理解到底是什么问题。
更新:
这是我描述的问题的工作代码示例。
楷模:
public class Car
{
public int Id { get; set; }
[Required]
[Display(Name = "Цена")]
public int Price { get; set; }
[Required]
[Display(Name = "Количество на складе")]
public int Count { get; set; }
public int? MarkId { get; set; }
public virtual Mark Mark { get; set; }
public int CarModelId { get; set; }
public virtual CarModel CarModel { get; set; }
public int? EquipmentId { get; set; }
public virtual Equipment Equipment { get; set; }
public virtual ICollection<Order> Orders { get; set; }
public Car()
{
Orders = new List<Order>();
}
}
public class Mark
{
public int Id { get; set; }
[Required]
[Display(Name = "Марка")]
public string MarkName { get; set; }
public virtual ICollection<CarModel> CarModels { get; set; }
public virtual ICollection<Car> Cars { get; set; }
public Mark()
{
CarModels = new List<CarModel>();
Cars = new List<Car>();
}
}
public class CarModel
{
public int Id { get; set; }
[Required]
[Display(Name = "Модель")]
public string ModelName { get; set; }
public int MarkId { get; set; }
public virtual Mark Mark { get; set; }
public virtual ICollection<Equipment> Equipments { get; set; }
public virtual ICollection<Car> Cars { get; set; }
public CarModel()
{
Equipments = new List<Equipment>();
Cars = new List<Car>();
}
}
public class Equipment
{
public int Id { get; set; }
//Двигателя
[Required]
[Display(Name = "Двигатель")]
public int Engine { get; set; }
//Количество лошадиных сил
[Required]
[Display(Name = "Мощность")]
public int Power { get; set; }
//Год выпуска
[Required]
[Display(Name = "Год выпуска")]
public int ReleaseYear { get; set; }
//Тип привода
[Required]
[Display(Name = "Тип привода")]
public string DriveType { get; set; }
//КПП
[Required]
[Display(Name = "КПП")]
public string Transmission { get; set; }
//Кузов
[Required]
[Display(Name = "Кузов")]
public string Body { get; set; }
//Максимальная скорость
[Required]
[Display(Name = "Максимальная скорость")]
public int MaxSpeed { get; set; }
//Вес
[Required]
[Display(Name = "Вес")]
public int Weight { get; set; }
//Бак
[Required]
[Display(Name = "Бак")]
public int MaxFuelVolume { get; set; }
//Цвет автомобиля
[Required]
[Display(Name = "Цвет")]
public string Color { get; set; }
//Изорбражение автомобиля
[Required]
[Display(Name = "Изображение автомобиля")]
public string Picture { get; set; }
public int CarModelId { get; set; }
public virtual CarModel CarModel { get; set; }
public virtual ICollection<Car> Cars { get; set; }
public Equipment()
{
Cars = new List<Car>();
}
}
在表格上,我添加了一辆新车,它具有其他车型的所有特征。我的表单,就像前面的例子一样,是由一个局部视图组成的。如果重要的话,我的局部视图窗口是模态的,并用 jquery 调用。他们来了:
_AddAuto:
@model AutoStore.Domain.Core.Car
<div id="AddAuto" class="modal fade">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">Добавление нового автомобиля</h4>
</div>
@using (Ajax.BeginForm(new AjaxOptions { OnSuccess = "AddCarSuccess", OnFailure = "AddCarError" }))
{
<div class="modal-body">
@Html.Partial("_CarAttributes")
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Закрыть</button>
<button id="btnConfirm" type="submit" class="btn btn-success">Добавить</button>
</div>
}
</div>
</div>
</div>
_汽车属性:
@model AutoStore.Domain.Core.Car
<div class="container">
<div class="row">
<div class="col-sm-4">
<div class="row">
<div class="col">
@Html.Label("Автомобиль:")
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.Price, "Цена")
@Html.EditorFor(i => i.Price, new { htmlAttributes = new { @id = "txtPrice", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Price, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.Count, "Количество на складе")
@Html.EditorFor(i => i.Count, new { htmlAttributes = new { @id = "txtCount", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Count, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.Mark.MarkName, "Марка")
@Html.EditorFor(i => i.Mark.MarkName, new { htmlAttributes = new { @id = "txtMarkName", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Mark.MarkName, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.CarModel.ModelName, "Модель")
@Html.EditorFor(i => i.CarModel.ModelName, new { htmlAttributes = new { @id = "txtModelName", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.CarModel.ModelName, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
<br />
@Html.LabelFor(i => i.Equipment.Picture, "Изображение Автомобиля", new { @style = "padding-right: 50px" })
@Html.EditorFor(i => i.Equipment.Picture, new { htmlAttributes = new { @class = "form-control", @type = "hidden", @id = "image" } })
<br />
@Html.ValidationMessageFor(i => i.Equipment.Picture, "", new { @id = "pictureError", @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
<input id="fileLoader" type="file" accept="image/*" />
<img id="autoPicture" class="img-responsive" />
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-sm-offset-1">
<div class="row">
<div class="col">
@Html.Label("Комплектация:")
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.Equipment.Engine, "Двигатель")
@Html.EditorFor(i => i.Equipment.Engine, new { htmlAttributes = new { @id = "txtEngine", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Equipment.Engine, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.Equipment.Power, "Мощность")
@Html.EditorFor(i => i.Equipment.Power, new { htmlAttributes = new { @id = "txtPower", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Equipment.Power, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.Equipment.ReleaseYear, "Год выпуска")
@Html.EditorFor(i => i.Equipment.ReleaseYear, new { htmlAttributes = new { @id = "txtYear", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Equipment.ReleaseYear, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.Equipment.DriveType, "Тип привода")
@Html.EditorFor(i => i.Equipment.DriveType, new { htmlAttributes = new { @id = "txtDriveType", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Equipment.DriveType, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.Equipment.Transmission, "КПП")
@Html.EditorFor(i => i.Equipment.Transmission, new { htmlAttributes = new { @id = "txtTransmission", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Equipment.Transmission, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.Equipment.Body, "Кузов")
@Html.EditorFor(i => i.Equipment.Body, new { htmlAttributes = new { @id = "txtBody", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Equipment.Body, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.Equipment.MaxSpeed, "Максимальная скорость")
@Html.EditorFor(i => i.Equipment.MaxSpeed, new { htmlAttributes = new { @id = "txtMaxSpeed", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Equipment.MaxSpeed, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.Equipment.Weight, "Вес")
@Html.EditorFor(i => i.Equipment.Weight, new { htmlAttributes = new { @id = "txtWeight", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Equipment.Weight, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.Equipment.MaxFuelVolume, "Бак")
@Html.EditorFor(i => i.Equipment.MaxFuelVolume, new { htmlAttributes = new { @id = "txtFuel", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Equipment.MaxFuelVolume, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
@Html.LabelFor(i => i.Equipment.Color, "Цвет")
@Html.EditorFor(i => i.Equipment.Color, new { htmlAttributes = new { @id = "txtColor", @class = "form-control" } })
@Html.ValidationMessageFor(i => i.Equipment.Color, "", new { @class = "text-danger" })
</div>
</div>
</div>
</div>
</div>
</div>
请注意,但是在 partialview _CarAttributes 上,为了让我通过 Car 模型收集模型中的所有数据,我在 EditorFor 中爬到其他模型中,例如@Html.EditorFor(i => i.Equipment.Weight
这是我的控制器:
[HttpPost]
public ActionResult _AddAuto(Car car)
{
if (car == null)
throw new Exception("Автомобиль не найден");
if (ModelState.IsValid)
{
unitOfWOrk.Cars.Create(car);
unitOfWOrk.Save();
return PartialView("_Car", car);
}
else
throw new Exception("Не все обязательные поля заполнены");
}
注意截图,所有数据不仅为 Car 模型填写,还为所有其他人填写,与我之前作为示例给出的 Weight 相同。如果您查看视图,您将看到一切。
这就是控制器中飞到我的地方:
Обратите внимание что хоть я и использую на вход модель Car но прилетают и все остальные модели. На последнем скриншоте я привёл пример только что помимо модели Car мне в контроллер пришла ещё и модель Equipment но там ещё пришли 2 модели, Mark и CarModel. Вот пример того что это работает.
Скрин:





У вас на классе CarModel указан MarkId, вот его и указывайте, а не Mark.Id раз вы пишете
@model CarModel.Код собрал из предоставленных вами классов, просто повырезал не относящееся к делу:
и убрав ajax:
Далее. Если вас интересует, чтобы некоторый паршиал _partial123 вводил модель Mark
то и пишите так, но первой строкой в _partial123 должно стоять @model Mark
Если предположить, что вы в _MarkModel хотите указать параметры для Mark - ну так и напишите @model Mark - но у вас же там снова стоит @model CarModel.
Но вообще, давайте прямо. У вас нужно ввести совершенно простую модель:
Это совершенно примитивнейшая модель. Я вижу, что у вас есть некоторые куски кода, которые позволяют не выбирать MarkId из существующего dropdown, а создать новый -- но если этого нет, то у вас абсолютно тривиальная модель из id, строкового поля и одного dropdown (см. мой код выше, там убрано всё лишнее).
Он абсолютно твириальный, там НЕТ НИКАКОЙ необходимости вводить Mark. При сохранении Id,ModelName, MarkId сохранится без проблем.
А вот если вы хотите сделать создание Mark на лету - вам нужно всего-навсего добавить ещё одну строку к модели:
И контроллер будет лишь чуть сложнее:
Поэтому в selectlist нужно добавить ещё одно значение, с нулём - для добавления нового значения. Я это сделаю так:
А вы воспользуйтесь перегрузкой у dropdown есть одна, где можно дополнительно добавить options.
И только когда поймёте эту систему -- только тогда и наворачивайте дополнительный переключатель создать новую марку/выбрать существующую (причём это вам даже не нужно делать полем модели CarModel - так и будете ориентироваться на MarkId = 0 чтобы создать модель). Просто будете в js дизаблить пункты на клиенте: если новая - то дизаблить дропдаун, если существующая - то поле с маркой скрывать. Но на модели это поле избыточно. Его не нужно передавать. Несмотря на наличие валидации на клиенте бекенд никогда не должен ей доверять (случаи поломанных скриптов, хакерские атаки и т.п.)
Эта система абсолютно проста и надёжна как автомат Калашникова. И только если у вас когда-нибудь попадётся сайт, где при создании машины можно будет указать сложный класс (Mark будет состоять не из одной строки, а из двух или пятнидцати) -- тогда и можно будет пойти совсем другим путём:
И вот только тогда у вас будет подключаться паршиал вот так:
@Html.Partial("_partial123", new Mark(...))
и в _partial123 будет стоять как @model Mark и вот только тогда у вас внутри этого паршиала будет НУЖНО писать
@Html.DropDownListFor(i => i.Idи у вас оно прилетит как NewMark.IdНо и в этом случае в контроллере вы будете проверять MarkId == 0 и если ноль, то создавать новый Mark из пришедших данных.
Но обратите внимание: в модели CarModel НЕЛЬЗЯ будет указывать
public Mark Mark { get; set; }и поле NewMark НЕЛЬЗЯ будет показывать EF'у, у вас CarModel будет вьюмоделью, EF вообще ничего не будет знать о такой сущности, вы будете только с Car и Mark работать.И вы эту разницу сейчас не понимаете - между моделью, которая нужна только для ввода (и её не сохраняют в базу) и моделью для EF.
У вас если будет слоистая структура приложения, то в проекте БД EF будет знать только о классах Car и Mark. А класс CarViewModel будет находиться application слое и дальше контроллера он вообще никуда не пойдёт. (Когда вам не понравятся распухшие от логики контроллеры ("контроллеры должны быть тонкими") - это можно будет поправить, но всё равно вы будете в application слое разбиать CarViewModel на отдельные кусочки и сохранять в базу только Car и/или Mark)
PS Есть ещё вариант: вы можете вообще удалить класс Car(View)Model из проекта. И работать только с Car и только с Mark:
Но и в этом случае вы будете работать с newMark только в случае если MarkId у car будет равен 0.
Проблема была в том, что у радиобаттона было такое же название как и у модели,
MarkИ это блокировало передачу модели в контроллер.