There is a problem with the transfer of data from the ajax request to the controller action.

Here are my models:

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>(); } } 

Here are my partial views:

_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">&times;</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> 

They show that I use Html.AjaxBegin.

Which leads to the action:

 [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("Не все обязательные поля заполнены"); } 

But when I fill in the fields, only the data on the car model arrives in the action, but the data on the make of the car does not arrive:

enter image description here

Although I have another form where Car is used in the same way ajax.beginform passes data to the controller. And there all data on all other models connected with the help of foreign key are tightened. Here is the car class:

 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>(); } } 

I can not understand what the difference? Please tell me if anyone sees what the problem is. Perhaps the case in the database architecture and entity in the case of car somehow guesses where to put some data, but in the case of carmodel there is no, but I can not understand what exactly is the case.

enter image description here

UPDATE:

Here is an example of my working code with the problem that I described.

Models:

 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>(); } } 

On the form, I perform the addition of a new car with all the characteristics that stretch from other models. My form as well as in the past case is composed of a partial view. If it matters, my window made up of a partial view is modal and invoked using jquery. Here they are:

_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">&times;</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> 

Notice that, but on partialview _CarAttributes, in order for me to have all the data gathered in the model through the Car model, I climb into other models in EditorFor, for example @Html.EditorFor(i => i.Equipment.Weight

And here is my controller:

 [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("Не все обязательные поля заполнены"); } 

enter image description here

Pay attention to the screenshot filled with all the data not only on the Car model but also on all the others, the same Weight that I cited as an example earlier. If you look at the View you will see everything.

And that's what flies me to the controller:

enter image description here

Please note that even though I use the Car model as input, all the other models also arrive. In the last screenshot, I gave an example that, in addition to the Car model, I also received the Equipment model, but there were also 2 models, Mark and CarModel. Here is an example of how it works.

Screen:

enter image description here

  • Well, I can’t say anything for another form, but on this one I immediately have a question: if you have a MarkId field on a class, then why in the view do you specify not Mark, I’m Mark.Id? - AK
  • @AK here I am collecting all the marks in the drop down list. I simply climb through the foreign key from the carmodel class to the mark class. If you about this. - Andrei
  • @AK I even tried to create a viewmodel where I shoved both CarModel and Mark for the sake of interest. Added to both view this viewmodel as an input model. And I also tied the fields I needed from the models to the fields. And in the end I do not even enter values, I press the add button. Code zalazit in action. And there I am just in shock. An instance of my viewmodel was created. With an empty CarModel, and Mark = null. That is, even taking into account that I did not submit the data at all, CarModel was created, but Mark was not. I can not understand at all what's the matter. What kind of mystique) - Andrew
  • @AK please take a look at the new question text. I wrote everything in detail how it works for me on another page. - Andrei
  • @AK If you can create a chat there it would be better to discuss it, if you certainly have time. - Andrei

2 answers 2

You have a MarkId on the CarModel class, so indicate it, not Mark.Id since you write @model CarModel .

enter image description here

The code collected from the classes you provided, just cut the irrelevant:

 @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> 

and removing ajax:

 @using (Html.BeginForm("_AddMarkModel", "Home", FormMethod.Post)) 

Further. If you are interested in some parshial _partial123 introduced the model Mark

 @Html.Partial("_partial123", new Mark(...)) 

write this, but the first line in _partial123 should be @model Mark

Assuming that you want to specify parameters for Mark in _MarkModel - well, just write @model Mark - but you have @model CarModel there again.

But generally, let's get straight. You need to enter a completely simple model:

 public class CarModel { public int Id { get; set; } public string ModelName { get; set; } public int MarkId { get; set; } } 

This is a completely primitive model. I see that you have some pieces of code that allow you not to select MarkId from the existing dropdown, but to create a new one - but if this is not the case, then you have an absolutely trivial model from id, string field and one dropdown (see my code above , there is removed all unnecessary).

It is absolutely tweetable, there is NO NO need to enter Mark. When saving Id, ModelName, MarkId will be saved without problems.

But if you want to create a Mark on the fly - you just need to add another line to the model:

 public class CarModel { public int Id { get; set; } public string ModelName { get; set; } public int MarkId { get; set; } public int NewMarkName { get; set; } } 

And the controller will be only slightly more complicated:

 [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(); } 

Therefore, in the selectlist, you need to add another value, with zero - to add a new value. I'll do it like this:

  var data = new[] { new SelectListItem { Text = "выберите чтобы создать новую модель", Value = "0" }, new SelectListItem { Text = "Bentley", Value = "1" }, new SelectListItem { Text = "Crysler", Value = "2" }, }; 

And you take advantage of the overload in the dropdown is one where you can optionally add options.

And only when you understand this system - only then turn the additional switch on to create a new brand / choose an existing one (and you don’t even have to do this with the CarModel model field - and you will be guided by MarkId = 0 to create a model). Just in js you will disabuse the items on the client: if the new one is, then drop the dropdown, if the existing one, then hide the field with the mark. But on the model this field is redundant. It does not need to pass. Despite the presence of validation on the client, the backend should never trust it (cases of broken scripts, hacker attacks, etc.)

This system is absolutely simple and reliable as a Kalashnikov assault rifle. And only if you ever get a site where, when creating a car, you can specify a complex class (Mark will not consist of one line, but two or fifteen) - then you will be able to go a completely different way:

 public class CarModel { public int Id { get; set; } public string ModelName { get; set; } public int MarkId { get; set; } public Mark NewMark { get; set; } } 

And only then you will have to connect the parshial like this:

@ Html.Partial ("_ partial123", new Mark (...))

and in _partial123 it will stand like @model Mark and only then you will need to write @Html.DropDownListFor(i => i.Id inside this parshiala @Html.DropDownListFor(i => i.Id and it will fly as NewMark.Id from you

But in this case, in the controller, you will check MarkId == 0 and if it is zero, then create a new Mark from the incoming data.

But pay attention: in the CarModel model it will NOT be possible to indicate public Mark Mark { get; set; } public Mark Mark { get; set; } public Mark Mark { get; set; } and the NewMark field will NOT be shown to EF, you will have a view model of CarModel, EF will not know anything about such an entity at all, you will only work with Car and Mark.

And you don’t understand this difference now - between a model that is needed only for input (and it is not stored in the database) and a model for EF.

If you have a layered structure of the application, then in the project, the EF DB will only know about the Car and Mark classes. And the CarViewModel class will be the application layer and it won't go any further than the controller. (When you do not like controllers swollen by logic (" controllers should be thin ") - this can be fixed, but you will still break the CarViewModel into separate pieces in the application layer and save only Car and / or Mark to the base)

PS There is another option: you can even remove the class Car (View) Model from the project. And work only with Car and only with Mark:

 public JsonResult Asdfgh(Car car, Mark newMark) 

But even in this case, you will work with newMark only if the car's MarkId is 0.

  • AK I tried as you said instead of Mark.Id for a drop down sheet to write MarkId. With a drop down sheet, everything is fine. All values ​​are pulled up there as before. But the data on the Mark model still hasn’t come to the action. The truth hasn’t tried to remove the ajax request yet, I’ll try it now, but something that I have a lot of encouragement won't change anything. - Andrei
  • @Andrey First, you said that your brand does not pull up, but the mark is uniquely determined by MarkId - apparently you needed to clearly indicate that you have a dropdown pull up (I don’t believe that you don’t have it at all on the debug window) Mark. Selecting a brand is enough to choose only id, because if you save to the database with this key, everything else will be pulled. And secondly, how can you not see that it is impossible for you to pull up the brand? That is, you expect Mark to come in a partial view, but you’re writing not @model Mark , but @model CarModel , understand that? - AK
  • I understand what you mean. I simply have either an existing brand from the drop-down list selected on the form, or a new one fits in. Screen in question. - Andrei
  • AK, you understood correctly, I want the data about Mark to be pulled up. Since I have another form in which the input model is Car. But 3 more models are being pulled up there. If necessary, I can give an example of the code of that page. - Andrei
  • Why on my screen there is no id Mark because I tried with the introduction of a new brand and not with the choice of a new one from the list. - Andrei

The problem was that the radio button had the same name as the model, Mark

 @Html.RadioButton("Mark", "NewMark", true) 

And it blocked the transfer of the model to the controller.