Monday, July 6, 2015

Avoid triggering control action multiple times Kendo UI Grid CRUD (for version 2015.2.624)

Sometimes the controller action will be triggered more than once if the DataSource of the Grid is not set up properly, here is how to produce the error message
  1. assume it is a master-detail update, the detail record has a unique index on a lookup field, and this lookup field will default to a value for the new record.
  2. Create a new record with the default value, save it. 
  3. Create a new record with the default value, again, save it. of course, it will fail with a error message
  4. Change the default value to something else, save it. at this moment you think that it should be saved properly. but it may fail if the DataSource is not setup properly, by using SQL Profiler you will find that TWO insert statements are issued to the server.

Here is how to avoid the above problem (assume doing User/UserRole update):
In the MVC Grid setting,  you need to make sure that Model.Id is setup properly
  
@(Html.Kendo().Grid()
    .Name("gridUserRole")
    .DataSource(dataSource => dataSource
        .Model(model =>
        {
            model.Id(p => p.UserRoleId);
            model.Field(p => p.UserId).DefaultValue(userId);
            model.Field(p => p.Role).DefaultValue(ViewBag.defaultRole as RoleViewModel);
            model.Field(p => p.RoleDescription).Editable(false);
        })
        .Read(read => read.Action("GetUserRoles", "Users", new { userId = userId }))
        .Create(create => create.Action("AddUserRole", "Users", new { userId = userId, roleId = "#=RoleId#" }))
        .Update(update => update.Action("UpdateUserRole", "Users"))
        .Destroy(destroy => destroy.Action("RemoveUserRole", "Users", new { userId = userId, roleId = "Administrator" }))
    )
    .Pageable()
    .Sortable())
 

In the Controller Action, the Id field need to be obtained and passed back to the view

public JsonResult AddUserRole([DataSourceRequest] DataSourceRequest request,
            UserRoleViewModel ur)
        {
            try
            {
                if (ur != null && ModelState.IsValid)
                {
                    UserRole newUR = new UserRole
                    {
                        UserId = ur.UserId,
                        RoleId = ur.Role.RoleId
                    };

                    newUR.UserRoleId = UserBll.AddUserRole(newUR);

                    ur.UserRoleId = newUR.UserRoleId;
                    ur.RoleId = ur.Role.RoleId;
                    ur.RoleDescription = UserBll.GetRoleDescription(ur.RoleId);
                }

                return Json(new[] { ur }.ToDataSourceResult(request, ModelState));
            }
            catch (System.Exception e)
            {
                ModelState.AddModelError("Role", ExceptionUtility.GetInnerMessage(e));
                return Json(ModelState.ToDataSourceResult(), JsonRequestBehavior.AllowGet);
            }
        }


and of course, you need to get the identity value for the newly created record in the BLL and send back to the controller action

public int AddUserRole(UserRole ur)
        {
            using (var dbContext = new InvestorContext())
            {
                dbContext.UserRoles.Add(ur);
                dbContext.SaveChanges();

                return ur.UserRoleId;
            }
        }

No comments: