null при отправке модели

282
05 мая 2017, 15:17

Всем здравствуйте. Столкнулся с проблемой:мне в приложении надо добавить товар в БД, при этом заранее не известно сколько у него будет разных описаний(характеристик) по этому есть 2 таблицы - Товары и Характеристики. При добавлении нового товара, надо отправлять на сервер форму товара и коллекцию характеристик. Я для этого сделал общую модель. Но когда я нажимаю submit и проверяю dev tools, там я вижу, что модель Товар отправляется, а Характеристики и близко нет. Где моя ошибка? Ниже выложу листинг моделей, кода сервера и вьюшки.

Модель товаров:

public partial class bs_parts
{
    public bs_parts()
    {
        this.bs_details = new HashSet<bs_details>();
        this.bs_images = new HashSet<bs_images>();
        this.bs_orders = new HashSet<bs_orders>();
    }
    public decimal parts_id { get; set; }
    public decimal parts_category_id { get; set; }
    public decimal parts_brand_id { get; set; }
    public string parts_model { get; set; }
    public double parts_price { get; set; }
    public int parts_amount { get; set; }
    public virtual bs_brands bs_brands { get; set; }
    public virtual bs_categories bs_categories { get; set; }
    public virtual ICollection<bs_details> bs_details { get; set; }
    public virtual ICollection<bs_images> bs_images { get; set; }
    public virtual ICollection<bs_orders> bs_orders { get; set; }
}

Модель характеристик:

public partial class bs_details
{
    public decimal details_id { get; set; }
    public string details_name { get; set; }
    public string details_value { get; set; }
    public decimal details_part_id { get; set; }
    public virtual bs_parts bs_parts { get; set; }
}

И общая модель:

public class AddPartViewModel
{
    public bs_parts part { get; set; }
    public List<bs_details> detail { get; set; }
    public bs_images image { get; set; }
}

Код серверной стороны:

[HttpPost]
    public ActionResult CreatePart(AddPartViewModel model, HttpPostedFileBase uploadImage)
    {
        ViewBag.Categories = new SelectList(_db.bs_categories, "categories_id", "categories_name");
        ViewBag.Brands = new SelectList(_db.bs_brands, "brands_id", "brands_name");
        if (ModelState.IsValid && model.part != null)
        {
            model.part.parts_brand_id = 3;
            model.part.parts_category_id = 3;
            _db.bs_parts.Add(model.part);
            if (model.detail != null)
            {
                foreach (var details in model.detail)
                {
                    details.details_part_id = 8;
                    _db.bs_details.Add(details);
                    _db.SaveChanges();
                }
            }
            if (ModelState.IsValid && uploadImage!=null)
            {
                byte[] imageData = null;
                using (var binaryReader = new BinaryReader(uploadImage.InputStream))
                {
                    imageData = binaryReader.ReadBytes(uploadImage.ContentLength);
                }
                model.image.image_part_id = model.part.parts_id;
                model.image.images_image = imageData;
                _db.bs_images.Add(model.image);
            }
            //добавить else if () {...} если модель есть, а изображения нет
            _db.SaveChanges();
            return RedirectToAction("Main");
        }
        return View(model);
    }

View:

<div>
        <p>
            Выбрать категорию:
            @Html.DropDownList("Categories");
        </p>
    </div>
    <div>
        <p>
            Выбрать производителя:
            @Html.DropDownList("Brands");
        </p>
    </div>
    <div>
        <p>
            Название модели:
            @Html.EditorFor(model=>model.part.parts_model)
            @Html.ValidationMessageFor(model=>model.part.parts_model)
        </p>
    </div>
    <div>
        <p>
            Цена:
            @Html.EditorFor(model=>model.part.parts_price)
            @Html.ValidationMessageFor(model=>model.part.parts_price)
        </p>
    </div>
    <div>
        <p>
            Количество:
            @Html.EditorFor(model=>model.part.parts_amount)
            @Html.ValidationMessageFor(model=>model.part.parts_amount)
        </p>
    </div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
    <form id="myForm" method="post" action="~/Controllers/AdminController.cs">
    <div class="details">
       <div class="details-part">
        <input class="details_name" type="text" />
        <input class="details_value" type="text" />
       </div>
    </div>
    <div><p><a class="plus">Add detail</a></p></div>
<div>
    <input type="file" name="uploadImage" />
</div>
<input type="hidden" name="details"/>
<div>
    <button id="save-form">Save</button>
</div>
</form>

Скрипт отправки и добавления новых полей:

<script>
var $myForm = $("#myForm");
$(".plus").off("click").on("click", function () {
    $(".details").append('<div class="details-part">  <input class="details_name" type="text" /> <input class="details_value" type="text" /> </div>')
});
$("#save-form").off("click").on("click", function () {
    var detailList = [];
    $myForm.find(".details-part").each(function () {
        detailList.push({
            details_value: $(this).find(".details_value").val(),
            details_name: $(this).find(".details_name").val()
        })
    });
    $myForm.find('[name="details"]').val(JSON.stringify(detailList))
    $myForm.submit()
});

В таком варианте detail приходит null, но если поменять и сделать так:

        <input name="detail[0].details_name" type="text" />
        <input name="detail[0].details_value" type="text" />
        <input name="detail[1].details_name" type="text" />
        <input name="detail[1].details_value" type="text" />
        <input name="detail[2].details_name" type="text" />
        <input name="detail[2].details_value" type="text" />

То на сервер приходит 3 объекта detail и все ок. Проблема будет, если я захочу удалить объект в середине, тогда порядок нарушится. Мне уже подсказали, что можно не удалять, а сделать поля hidden, а на сервере уже делать проверку на пустое поле. Я в JS не в зуб ногой, может кто-то, у кого есть свободное время и желание, помочь написать скрипт такой, чтобы он добавлял новые поля и при "удалении" делал их hidden. С добавлением я справлюсь, а вот со вторым проблемы.

Answer 1

для случая, когда вы генерируете инпуты для списка объектов, и не гарантируете последовательность индексов (например при удалении некоторых элементов), то можете использовать свойство index:

<input name="detail.Index" type="hidden" value="0"/>     
<input name="detail[0].details_name" type="text" />
<input name="detail[0].details_value" type="text" />
<input name="detail.Index" type="hidden" value="1"/>     
<input name="detail[1].details_name" type="text" />
<input name="detail[1].details_value" type="text" />
<input name="detail.Index" type="hidden" value="2"/>     
<input name="detail[2].details_name" type="text" />
<input name="detail[2].details_value" type="text" />

с использованием этого поля в вашу модель попадут все данные, вне зависимости от пропусков номеров. В общем случае, ваши индексы могут быть даже не числовыми, например:

<input name="detail.Index" type="hidden" value="test"/>     
<input name="detail[test].details_name" type="text" />
<input name="detail[test].details_value" type="text" />
READ ALSO
Работа атрибута DataSource в #

Работа атрибута DataSource в #

Не могу найти хорошего объяснения работы атрибута [DataSource]Везде работа с таблицами

225
Выборка участка данных из БД

Выборка участка данных из БД

Есть проект использующий C#NET, Entity Framework

204
Как сделать запрос с Request Payload?

Как сделать запрос с Request Payload?

Скажите пожалуйста как на c# сделать Request Payload запрос? Я просто впервые за всю жизнь встретил такой тип запроса :(

245
Событие при выборе элемента из списка combobox WPF

Событие при выборе элемента из списка combobox WPF

Есть комбобокс на WPFВ боксе несколько значений нужно что бы при выборе одного запускался один метод а при выборе другого значения - соответственно...

600