Есть проблема с передачей данных из 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 передаёт данные в контроллер. И там подтягиваются все данные по всем остальным связанным с помощью foreign key моделям. Вот класс car:
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>();
}
}
Не как не могу понять в чём разница? Подскажите пожалуйста если кто видит в чём проблема. Возможно дело в архитектуре базы данных и entity в случае с car каким то образом догадывается куда какие данные засунуть, а в случае с carmodel нет, но в чём конкретно дело понять не могу.
UPDATE:
Вот пример моего работающего кода с той проблемой что я описал.
Модели:
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>();
}
}
На форме я выполняю добавление нового автомобиля со всеми характеристиками что тянутся от других моделей. Моя форма так же как и в прошлом случае составляется из partial view. Если это имеет значение, моё окно составленное из partial view является модальным и вызывается с помощью 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>
_CarAttributes:
@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 но и по всем остальным, тот же Вес который я привёл для примера ранее. Если посмотрите на View всё увидите.
И вот что мне прилетает в контроллер:
Обратите внимание что хоть я и использую на вход модель Car но прилетают и все остальные модели. На последнем скриншоте я привёл пример только что помимо модели Car мне в контроллер пришла ещё и модель Equipment но там ещё пришли 2 модели, Mark и CarModel. Вот пример того что это работает.
Скрин:
У вас на классе CarModel указан MarkId, вот его и указывайте, а не Mark.Id раз вы пишете @model CarModel
.
Код собрал из предоставленных вами классов, просто повырезал не относящееся к делу:
@model WebApplication1.Models.CarModel
@{
var data = new[]
{
new SelectListItem { Text = "1", Value = "1" },
new SelectListItem { Text = "2", Value = "2" },
};
}
<div class="container">
@Html.DropDownListFor(i => i.MarkId, data)
</div>
и убрав ajax:
@using (Html.BeginForm("_AddMarkModel", "Home", FormMethod.Post))
Далее. Если вас интересует, чтобы некоторый паршиал _partial123 вводил модель Mark
@Html.Partial("_partial123", new Mark(...))
то и пишите так, но первой строкой в _partial123 должно стоять @model Mark
Если предположить, что вы в _MarkModel хотите указать параметры для Mark - ну так и напишите @model Mark - но у вас же там снова стоит @model CarModel.
Но вообще, давайте прямо. У вас нужно ввести совершенно простую модель:
public class CarModel
{
public int Id { get; set; }
public string ModelName { get; set; }
public int MarkId { get; set; }
}
Это совершенно примитивнейшая модель. Я вижу, что у вас есть некоторые куски кода, которые позволяют не выбирать MarkId из существующего dropdown, а создать новый -- но если этого нет, то у вас абсолютно тривиальная модель из id, строкового поля и одного dropdown (см. мой код выше, там убрано всё лишнее).
Он абсолютно твириальный, там НЕТ НИКАКОЙ необходимости вводить Mark. При сохранении Id,ModelName, MarkId сохранится без проблем.
А вот если вы хотите сделать создание Mark на лету - вам нужно всего-навсего добавить ещё одну строку к модели:
public class CarModel
{
public int Id { get; set; }
public string ModelName { get; set; }
public int MarkId { get; set; }
public int NewMarkName { get; set; }
}
И контроллер будет лишь чуть сложнее:
[HttpPost]
public JsonResult _AddMarkModel(CarModel model)
{
if (model == null)
throw new Exception("Модель не найдена");
var car = new Car
{
ModelName = model.ModelName,
MarkId = model.MarkId,
};
if (model.MarkId == 0)
{
var mark = new Mark{MarkName = model.NewModelName};
this.Db.Marks.Add(mark);
this.Db.SaveChanges();
car.MarkId = mark.Id;
}
this.Db.Cars.Add(car);
this.Db.SaveChanges();
}
Поэтому в selectlist нужно добавить ещё одно значение, с нулём - для добавления нового значения. Я это сделаю так:
var data = new[]
{
new SelectListItem { Text = "выберите чтобы создать новую модель", Value = "0" },
new SelectListItem { Text = "Bentley", Value = "1" },
new SelectListItem { Text = "Crysler", Value = "2" },
};
А вы воспользуйтесь перегрузкой у dropdown есть одна, где можно дополнительно добавить options.
И только когда поймёте эту систему -- только тогда и наворачивайте дополнительный переключатель создать новую марку/выбрать существующую (причём это вам даже не нужно делать полем модели CarModel - так и будете ориентироваться на MarkId = 0 чтобы создать модель). Просто будете в js дизаблить пункты на клиенте: если новая - то дизаблить дропдаун, если существующая - то поле с маркой скрывать. Но на модели это поле избыточно. Его не нужно передавать. Несмотря на наличие валидации на клиенте бекенд никогда не должен ей доверять (случаи поломанных скриптов, хакерские атаки и т.п.)
Эта система абсолютно проста и надёжна как автомат Калашникова. И только если у вас когда-нибудь попадётся сайт, где при создании машины можно будет указать сложный класс (Mark будет состоять не из одной строки, а из двух или пятнидцати) -- тогда и можно будет пойти совсем другим путём:
public class CarModel
{
public int Id { get; set; }
public string ModelName { get; set; }
public int MarkId { get; set; }
public Mark NewMark { get; set; }
}
И вот только тогда у вас будет подключаться паршиал вот так:
@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:
public JsonResult Asdfgh(Car car, Mark newMark)
Но и в этом случае вы будете работать с newMark только в случае если MarkId у car будет равен 0.
Проблема была в том, что у радиобаттона было такое же название как и у модели, Mark
@Html.RadioButton("Mark", "NewMark", true)
И это блокировало передачу модели в контроллер.
Виртуальный выделенный сервер (VDS) становится отличным выбором
Я делаю некое подобие игры на wpf, и мне интересно как можно сделать, чтоб другие программы определяли моё приложение как игру? Чтоб, например,...
Как мне убрать MouseButtonStatePressed с кнопки мышки програмно? Т
Есть окно с 26 кнопкамиКаждая кнопка - одна буква английского алфавита(A-Z)