CristiC777
CristiC777

Reputation: 481

Asp.net Core EF 3DropDown in series on Edit form, Save will delete fields in navigation properties tabels

I have 4 entities : Vehicle, VehicleBrand, VehicleModel, VehicleVersion

enter image description here

in one-to-many relationship, code-first generated by ef.
Database look like that: enter image description here

Edit form use jQuery, first dropdown is feeded by a ViewBag, and the other 2 by Json.

The Edit Form is that: enter image description here

And when I save the DropDowns retrn ONLY id ! dooh !
But my Bind expect entity
enter image description here

As you can see vbName came back null and after SaveChanges.. in all entities have name field null
enter image description here

Here is the Controller code for Edit:

[HttpGet]
    public async Task<IActionResult> YourEditNewCar(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }
        var brands = await _context.VehicleBrands.OrderBy(b => b.vbName).Select(x => new { Id = x.id, Value = x.vbName }).ToListAsync();
        var models = await _context.VehicleModels.OrderBy(m => m.vmName).ToListAsync();
        var versions= await _context.VehicleVersions.OrderBy(v =>v.vvName).ToListAsync();

        var model = new Vehicle();         

        ViewBag.BrandList =  new SelectList(brands, "Id", "Value");

        model = await _context.Vehicles.SingleOrDefaultAsync(m => m.id == id);
        if (model == null)
        {
            return NotFound();
        }
        return View("~/Views/Manage/YourEditNewCar.cshtml", model);                         
    }


    public JsonResult getVehicleModelById(int id)
    {
        List<VehicleModel> vehicleModelList = new List<VehicleModel>();
        vehicleModelList = _context.VehicleModels.Where(m => m.VehicleBrand.id == id).OrderBy(m => m.vmName).ToList();          //.Select(y => new { Id = y.id, Value = y.vmName })
        vehicleModelList.Insert(0, new VehicleModel { id = 0, vmName = "Car Model" });

        return Json(vehicleModelList);
    }

    public JsonResult getVehicleVersionById(int id)
    {
        List<VehicleVersion> vehicleVersionList = new List<VehicleVersion>();
        vehicleVersionList = _context.VehicleVersions.Where(m => m.VehicleModel.id == id).OrderBy(m => m.vvName).ToList();          //.Select(y => new { Id = y.id, Value = y.vmName })
        vehicleVersionList.Insert(0, new VehicleVersion { id = 0, vvName = "Car Version" });

        return Json(vehicleVersionList);
    }

and the post:

   [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> YourEditNewCar(int id, [Bind("id,DoorsNr,isAvailable,isDamaged,isDeleted,FabricationDate,FuelTankCapacity,TrunckCapacity,OnBoardKm,SeatNr," +
                                                                    "LicencePlate, VehicleBrand, VehicleModel, VehicleVersion")] Vehicle vehicle)
    {
        var user = await _userManager.GetUserAsync(User);

        if (ModelState.IsValid)
        {
            try
            {
                _context.Update(vehicle);
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!VehicleExists(vehicle.id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return RedirectToAction("YourCar", "Manage");
        }
        return View(vehicle);
    }

in the Edit form dropdowns look like this:

  <hr>
        <div class="col-md-12 text-center"><h4> Please select :</h4></div>

        <div class="row row-list">
            <div class="form-group col-xs-4">
                <label asp-for="VehicleBrand" class="control-label hidden" value=""></label>
                @Html.DropDownListFor(model => model.VehicleBrand.id, (SelectList)ViewBag.BrandList, "Car Brand", new { style = "width: 140px;", @class = "form-control" })
           </div>

            <div class="form-group  col-xs-4">
                <label asp-for="VehicleModel" class="control-label hidden" value=""></label>
                @Html.DropDownListFor(model => model.VehicleModel.id, new SelectList(string.Empty), "Car Model", new { style = "width: 120px;", @class = "form-control" })
            </div>

            <div class="form-group  col-xs-4">
                <label asp-for="VehicleVersion" class="control-label hidden" value=""></label>
                @Html.DropDownListFor(model => model.VehicleVersion.id, new SelectList(string.Empty), "Car Version", new { style = "width: 110px;", @class = "form-control" })
            </div>
        </div>

and the scrip from edit form, to feed dropdowns, is like that:

<script>
$(function () {
    $("#VehicleBrand_id").change(function () {
        //alert("Vehicle Brand dd changed !");
        var url = '@Url.Content("~/Manage/getVehicleModelById")';
        var ddlsource = "#VehicleBrand_id";

        $.getJSON(url, { id: $(ddlsource).val() }, function (data) {
            var items = '';
            $("#VehicleModel_id").empty();
            $.each(data, function (i, row) {
                items += "<option value='" + row.id + "'>" + row.vmName + "</option>";
            });
            $("#VehicleModel_id").html(items);
        })
    });
});
</script>

All dropdowns works perfect, the return is just id.
And when I SaveChanges, even if I want to save Only in Vehicle table, it update in other entities too, by navigation properties.

 _context.Update(vehicle);
  await _context.SaveChangesAsync();

I tried this to not Update VehicleBrands, but doesn't have effect :
enter image description here

and this need a new object:
enter image description here

I feel that I'm missing something simple, or is a wrong approach.

The problem Was in Model! I used Just: public VehicleBrand VehicleBrand { get; set; } Without declaring field as ForeignKey
I added in model:
[ForeignKey("VehicleVersionid")]
public int VehicleVersionid { get; set; }

Now I added (with Bold) in Model:

[ForeignKey("VehicleBrandid")]
public int VehicleBrandid { get; set; }

public VehicleBrand VehicleBrand { get; set; }

[ForeignKey("VehicleModelid")]
public int VehicleModelid { get; set; }

public VehicleModel VehicleModel { get; set; }

[ForeignKey("VehicleVersionid")]
public int VehicleVersionid { get; set; }

public VehicleVersion VehicleVersion { get; set; }

And edited in :
[HttpPost]
[ValidateAntiForgeryToken]
public async Task YourEditNewCar(int id,[Bind("id,DoorsNr,isAvailable,isDamaged,isDeleted,FabricationDate,FuelTankCapacity,TrunckCapacity,OnBoardKm,SeatNr," + "LicencePlate, VehicleBrandid, VehicleModelid, VehicleVersionid")] Vehicle vehicle)

Thanks for your patience!:)
Works like a charm ! :)

Upvotes: 1

Views: 322

Answers (2)

Saneesh kunjunni
Saneesh kunjunni

Reputation: 548

The issue with the code is, mvc modelstate validation processing with respected to the model used in your posted view. So in this your model is vehicles, so with respect vehicle we need to give values for model. vehicleBrand, VehicleModel, VehicleVersion are different model and the value in that field not have any dependencies on Vehicles model at posting time. so in view you need to use like this

<div class="row row-list">
        <div class="form-group col-xs-4">
            <label asp-for="VehicleBrand" class="control-label hidden" value=""></label>
            @Html.DropDownListFor(model => model.VehicleBrandid, (SelectList)ViewBag.BrandList, "Car Brand", new { style = "width: 140px;", @class = "form-control" })
       </div>

        <div class="form-group  col-xs-4">
            <label asp-for="VehicleModel" class="control-label hidden" value=""></label>
            @Html.DropDownListFor(model => model.VehicleModelid, new SelectList(string.Empty), "Car Model", new { style = "width: 120px;", @class = "form-control" })
        </div>

        <div class="form-group  col-xs-4">
            <label asp-for="VehicleVersion" class="control-label hidden" value=""></label>
            @Html.DropDownListFor(model => model.VehicleVersionid, new SelectList(string.Empty), "Car Version", new { style = "width: 110px;", @class = "form-control" })
        </div>
    </div>

and once one model used in view it is will we changed the whole property as entity then it be difficult to manage and check entitystate.unchanged property

Upvotes: 1

DafyddNZ
DafyddNZ

Reputation: 258

You should be using

@Html.DropDownListFor(model => model.VehicleBrandid, (SelectList)ViewBag.BrandList, "Car Brand", new { style = "width: 140px;", @class = "form-control" })

not

@Html.DropDownListFor(model => model.VehicleBrand.id, (SelectList)ViewBag.BrandList, "Car Brand", new { style = "width: 140px;", @class = "form-control" })

Bind your dropdown list to the id field in vehicle entity, not to the id field in the related entity.

Or if you wanted to start using taghelpers in asp.net core you could use this reference: https://learn.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms#the-select-tag-helper

Upvotes: 1

Related Questions