0
votes

I have a KendoUI Grid that shows related data via a view in my SQL database. The two tables that it shows are Cars and Bookings

Table: Cars

  • public int Id
  • public string Reg
  • public string Make
  • public string Model
  • public string Type
  • public string Fuel

Table: Bookings

  • public int Id
  • public string Notes
  • public DateTime BookingStart
  • public DateTime BookingEnd
  • public int Car_Id

The code for the grid is:

@(Html.Kendo().Grid<MyProject.ViewModels.CarBookings>()
.Name("Bookings")
.Columns(columns => {
    columns.Bound(c => c.Id);
    columns.Bound(c => c.BookingId);
    columns.Bound(c => c.Reg);
    columns.Bound(c => c.Make);
    columns.Bound(c => c.Model);
    columns.Bound(c => c.BookingStart).Format("{0:dd/MM/yyyy}");
    columns.Bound(c => c.BookingEnd).Format("{0:dd/MM/yyyy}");
    columns.Command(command => { command.Edit(); }).Width(250);
})
.Pageable()
.Sortable()
.Groupable()
.Editable(editable => editable.Mode(GridEditMode.InLine))
.DataSource(dataSource => dataSource
    .Ajax()
    .Model(model => {
        model.Id(p => p.Id);
        model.Field(p => p.Id).Editable(false);
        model.Field(p => p.Car_Id).Editable(false);
    })
    .Read(read => read.Action("GetBookings", "Bookings"))
                        .Update(update => update.Action("UpdateBookings", "Bookings", new { BookingId = "#=BookingId#" })))

))

We allow the user to use the inline editing features of the grid and update details. The problem I have is when changes are submitted from the grid the Update methods parameter is the entire model (Cars and Bookings) which overwrites all fields that are NOT in the grid as null, effectively wiping out my data except for the item that was updated.

To get around this I thought I could use [Bind(Include = "")] and specify what fields I want to update and EF would leave the rest as they are but it's not working.

Here is the UpdateBooking method with a very crude attempt to update both tables and specify what fields I want to update.

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult UpdateBookings([DataSourceRequest] DataSourceRequest request, [Bind(Include = "Reg, Make, Model")]  Cars car, [Bind(Include = "BookingStart, BookingEnd")] Bookings booking, int BookingId, BookingsViewModel vm)
    {
        if (ModelState.IsValid)
        {
            unitOfWork.CarRepository.Update(car);                
            booking.Id = BookingId;
            unitOfWork.BookingRepository.Update(booking);
            unitOfWork.Save();
        }

        return Json(new[] { vm }.ToDataSourceResult(request, ModelState));
    }

When you try to update, the parameter passes the entire model for both car and booking and sets the fields that are not in the grid to null which wipes out existing data. Is there any way I can tell my method to only update the fields that are passed in from the grid and leave the rest of my data intact?

1
In general, I would just work with the view model (don't pass entities). So now in your update method fetch the car and booking and then replace the updated fields (automapper great for this). Otherwise you will need to include all the car and booking fields in your grid definition (you can hide them from display). - Steve Greene
Thanks @SteveGreene, my concern was having to bring back all the records and just hide them. Could you give me an example of the approaching using the viewmodel? Many thanks - Yanayaya

1 Answers

1
votes

This is what we do.

public JsonResult Update([DataSourceRequest] DataSourceRequest request, MyViewModel myViewModel)
{
    if (ModelState.IsValid)
    {
        // Get 1st entity to update
        var myEntity = _db.MyEntities.Single(s => s.MyID == myViewModel.MyID);

        // Automapper will copy the viewmodel fields to the entity (could do manually as well)
        Mapper.Map(myViewModel, myEntity);

        // Repeat for 2nd entity...

        _db.SaveChanges();

        // May need to refresh viewmodel if other fields were affected by insert/update
    }

    return Json(new[] { myViewModel }.AsQueryable().ToDataSourceResult(request, ModelState));
}