Batch Grid Update causes data to be updated multiple times

3 posts, 0 answers
  1. Bill
    Bill avatar
    19 posts
    Member since:
    Jul 2018

    Posted 22 Jul 2019 Link to this post

    User fill out some search criteria to populate an in-cell batch editable grid.  After making all of their changes they click a save button.  This triggers the grid update which in turn calls a controller that updates the data.  On request call the read action passing in the search criteria(to refresh the grid).

    The update SQL updates the current task to complete, and if it meets certain criteria, inserts new child records.

    What I am seeing in the database is that the same records are being updated many times.  That isn't great but what is worse is that new child records are being created.

    What do I have wired incorrectly?  Is update being called multiple times?  Are records not getting their modified flag cleared?

    Here is my view and controller.  They are stripped down but should have adequate info.

    public class ViewPage1Controller : Controller
        {

            private readonly RmsContext2 _context;
            RmsContext2 context = new RmsContext2(null);
            FieldManagementContext contextFM = new FieldManagementContext(null);

            public ViewPage1Controller(RmsContext2 context)//IMediator mediator)
            {
                _context = context;
            }

            public ActionResult Search([DataSourceRequest] DataSourceRequest request, SearchCriteria criteria)
            {
                var param13 = new SqlParameter { ParameterName = "DateType", Value = criteria.DateType };
                var result = context.Database.SqlQuery<SearchResult>("PR_Review_Results @ToPortalId, @ProjectIds, @StoreId, @StatusId, @TypeId, @FromPortalId, @DistrictId, @DateFrom, @DateTo, @IncludeOutstanding, @ReportTag, @IncludeUnsubmit, @DateType",
                        param, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13);
                return Json(result.ToList().ToDataSourceResult(request), "application/json", System.Text.Encoding.UTF8, JsonRequestBehavior.AllowGet);
            }

            public ActionResult Update([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<SearchResult> results, SearchCriteria criteria)
            {
                if (ModelState.IsValid)
                {
                    //for each updated record
                    //update image status, description and date
                    //update task status and date
                    //create any new tasks as children for rejected
                    using (var db = new RmsContext2(null))
                    {
                        foreach (var result in results)
                        {
                            db.Database.ExecuteSqlCommand("PR_Review_Update @PortalAccountId, @ActivityId, @ImageId, @ImageStatus, @Description, @Comment, @FollowUpDate, @FeedbackId, @TemplateId, @FollowUpActivityTypeId, @RefImageId, @IncludeMaster",
                                param, param2, param4, param5, param6, param7, param8, param3, param9, param10, param11, param12);

                        }
                    }

                }
                return Json(results.ToDataSourceResult(request, ModelState));
            }
        }

     

    @model IEnumerable<PortalApp.Areas.TaskManagement.Features.AdvPhotoReview.Index.SearchResult>
    @(Html.Kendo().Grid(Model)
        .Name("Grid")
        .Deferred(true)
        .Events(events => events.DataBound("databound"))
        .Columns(columns =>
        {
            columns.Bound(p => p.SmallImage64)
                .ClientTemplate("<a href='../TaskManagement/AdvPhotoReview/Compare?imageId=#=ImageId#' target='blank'><img src='data:image/png;base64,#=SmallImage64#' onmouseover='showCompare(event)'  onmouseout='hideCompare(event)' /></a><br /><img src='../Images/SmallIcons/Flip.png' onclick='flipImage(event)' /><img src='../Images/SmallIcons/RotateLeft.png' onclick='rotateleftImage(event)' /><img src='../Images/SmallIcons/RotateRight.png' onclick='rotaterightImage(event)' />")
                .Title("Photo").Width(100).Filterable(false).Editable("imageEditable").Sortable(false).HtmlAttributes(new { style = "text-align: center;" });
            columns.Template(@<text></text>)
                .ClientTemplate(
                                "# var temp = AllComments; if (temp.length > 0) { #" +
                                    "<img src='../Images/48X48/comments.png' onclick='showComments(event)' />" +
                                "# } else { #" +
                                    "<img src='' />" +
                                "# } #"
                ).Title("Messages")
                .HeaderTemplate("").Width(70).HtmlAttributes(new { style = "text-align: center;" });
            columns.Template(@<text></text>)
                .ClientTemplate(@"<input name='name#=ActivityId#' type='radio' value='5' #= ImageStatusId==5 ? checked='checked':'' # /> Received
                        </br><input name='name#=ActivityId#' type='radio' value='2' #= ImageStatusId==2 ? checked='checked':'' # /> Reviewed - Release to Vendor
                        </br><input name='name#=ActivityId#' type='radio' value='1' #= ImageStatusId==1 ? checked='checked':'' # /> Reviewed - Do not release to Vendor
                        </br><input name='name#=ActivityId#' type='radio' value='3' #= ImageStatusId==3 ? checked='checked':'' # /> Rejected")
                .HeaderTemplate("Photo Status").Width(240).HeaderHtmlAttributes(new { style = "background-color:white;" }); ;
            columns.Bound(p => p.ActivityId).Title("ActivityId").Hidden();
            columns.Bound(p => p.ImageId).Title("ImageId").Hidden();
            columns.Bound(p => p.ActivityType).Title("ActivityType").Hidden();
            columns.Bound(p => p.ReportTag).Title("Report Tag").Width(100).Filterable(false);
            columns.Bound(p => p.Question).Title("Store Report Question").Width(200).Filterable(false);
            columns.Bound(p => p.Description).Title("Photo Description").Width(200).Filterable(false).HeaderHtmlAttributes(new { style = "background-color:white;" });
            columns.Bound(p => p.ReportComments).Title("Report Comments").Width(200).Filterable(false);
            columns.Bound(p => p.Answer).Title("Answer").Hidden();
            columns.Bound(p => p.SubmissionDate).Title("Submission Date").Format("{0:MM/dd/yyyy hh:mm tt}").Width(140).Filterable(false);
            columns.Bound(p => p.Confirmation).ClientTemplate("<a href='../Servicing/ViewServiceReport.aspx?Id=#=Confirmation#' target='_blank'>#=Confirmation#</a>").Title("Confirmation")
                .HeaderTemplate("Confirmation").Width(90).Filterable(false);
            columns.Bound(p => p.ProjectId).ClientTemplate("<a href='../Servicing/ViewProject.aspx?Id=#=ProjectId#' target='_blank'>#=ProjectNumber#</a>").Title("View Project")
                .HeaderTemplate("Project").Width(70).Filterable(false);
            columns.Bound(p => p.RmsDistrictId).Title("RmsDistrictId").Hidden();
            columns.Bound(p => p.StoreId).ClientTemplate("<a href='../Servicing/ViewStore.aspx?Id=#=StoreId#' target='_blank'>#=StoreDisplay#</a>").Title("View Store")
                .HeaderTemplate("Store").Width(70).Filterable(false);
            columns.Bound(p => p.ToPortalId).Title("ReviewerId").Hidden();
            columns.Bound(p => p.FromPortalId).ClientTemplate("<a href='../Servicing/ViewEmployee.aspx?Id=#=FromRecordId#' target='_blank'>#=FromDisplayName#</a>").Title("View Rep")
                .HeaderTemplate("Rep").Width(70).Filterable(false);
            columns.Bound(p => p.ColorBox).Title("ColorBox").Hidden();
            columns.Template(@<text></text>).ClientTemplate(GetActions().ToString()).HtmlAttributes(new { @class = "menuCell", @style = "width:95px;" }).Width(90);

            })
        .ToolBar(toolbar =>
        {
        toolbar.Template(@<text>
                    <a href="javascript:void(0)" class="k-button k-primary k-button-icontext k-grid-save-changes" title="Save"><span class="k-icon k-i-check"></span>Save</a>
                    <a href="javascript:void(0)" class="k-button k-button-icontext k-grid-cancel-changes" title="Cancel"><span class="k-icon k-i-cancel"></span>Cancel</a>
                    <span style="float:right">
                        Default Photo Status:
                        @(Html.Kendo().ComboBox()
                        .Name("defaultStatus")
                        .BindTo(new List<string>() {
                            "",
                            "Reviewed - Release to Vendor"
                        })
                        .Events(events => events.Close("onDefaultSelect"))
                        .SelectedIndex(0)
                        .Suggest(true)
                        .AutoWidth(true)
                        .Deferred(true)
                    .HtmlAttributes(new { @class = "k-content", style = "width:250px;" })
                        )
                    </span></text>);
        })
        .HtmlAttributes(new { style = "height: 730px;" })
        .NoRecords()
        .Pageable(pageable => pageable
            .Input(true)
            .Numeric(false)
            .PageSizes(new List<object> { 5, 10, 20, 30, 40, 50 })
            )
        .Sortable()
        .Filterable()
        .Scrollable()
        .DataSource(dataSource => dataSource
            .Ajax()
            .Events(events => events.Error("error_handler"))
            .Events(events =>
            {
                events.RequestEnd("onRequestEnd");
            })
            .Batch(true)
            .Model(model =>
            {
                model.Id(i => i.ActivityId); // Specify the property which is the unique identifier of the model.
                model.Field(i => i.ImageId).Editable(false);
                model.Field(i => i.Thumbnail).Editable(false);
                model.Field(i => i.Question).Editable(false);
                model.Field(i => i.Answer).Editable(false);
                model.Field(i => i.ActivityType).Editable(false);
                model.Field(i => i.SubmissionDate).Editable(false);
                model.Field(i => i.ProjectId).Editable(false);
                model.Field(i => i.ReportComments).Editable(false);
                model.Field(i => i.Confirmation).Editable(false);
                model.Field(i => i.RmsDistrictId).Editable(false);
                model.Field(i => i.StoreId).Editable(false);
                model.Field(i => i.FromPortalId).Editable(false);
                model.Field(i => i.ToPortalId).Editable(false);
                model.Field(i => i.ProjMgrPortalId).Editable(false);
                model.Field(i => i.FromRecordId).Editable(false);
                model.Field(i => i.SmallImage64);//.Editable(false);
                model.Field(i => i.ToDisplayName).Editable(false);
                model.Field(i => i.FromDisplayName).Editable(false);
                model.Field(i => i.ProjMgrDisplayName).Editable(false);
                model.Field(i => i.ReportTag).Editable(false);
            })
            .PageSize(20)
            .Sort(s =>
            {
                s.Add("ProjectId").Ascending();
                s.Add("SubmissionDate").Ascending();

            })
            .ServerOperation(false)
            .Update(update => update.Action("Update", "AdvPhotoReview").Data("additionalData"))
            .Read(read => read.Action("Search", "AdvPhotoReview"))
        )
        .Editable(editable => editable.Mode(GridEditMode.InCell))
    )
    @helper  GetActions()
    {
        @(Html.Kendo()
            .Menu()
            .Name("HoverMenu_#=ActivityId#")
            //.Deferred(true)
            .Direction(MenuDirection.Right)
            .Orientation(MenuOrientation.Vertical)
            .Animation(false)
            .Items(
                items => items.Add().Text("Contact").HtmlAttributes(new { @class = "k-menu-actions" }).Items(
                    innerItems =>
                    {
                        innerItems.Add().Text("Rep").HtmlAttributes(new { onclick = "showContact(event)" });
                        innerItems.Add().Text("Account Manager").HtmlAttributes(new { onclick = "showUnsubmit(event)" });
                    }
                )
            ).ToClientTemplate())
    }

    <script>
        function onRequestEnd(e) {
            if (e.type == "update") {
                var grid = $('#Grid').data('kendoGrid');
                var strProjects = getProjects();
                grid.dataSource.read({
                    Store: $('#stores').data('kendoComboBox').value(),
                    Type: $('#type').data('kendoComboBox').value(),
                    Rep: $('#rep').data('kendoComboBox').value(),
                    District: $('#district').data('kendoComboBox').value(),
                    DateFrom: $('#datefrom').data('kendoDatePicker').value(),
                    DateTo: $('#dateto').data('kendoDatePicker').value(),
                    Project: strProjects,
                    IncludeOutstanding: $('#includeoutstanding').is(':checked'),
                    ReportTag: $('#reporttag').data('kendoComboBox').value(),
                    IncludeUnsubmit: $('#includeunsubmit').is(':checked'),
                    DateType: $('#datetype').data('kendoDropDownList').text()
                });
                var grid = $("#Grid").data("kendoGrid");
                if (grid.dataSource.page() > 1) {
                    grid.dataSource.page(1);
                }
            }
        }
        function additionalData() {
            var strProjects = getProjects();
            return {
                Store: $('#stores').data('kendoComboBox').value(),
                Type: $('#type').data('kendoComboBox').value(),
                Rep: $('#rep').data('kendoComboBox').value(),
                District: $('#district').data('kendoComboBox').value(),
                DateFrom: $('#datefrom').data('kendoDatePicker').value(),
                DateTo: $('#dateto').data('kendoDatePicker').value(),
                Project: strProjects,
                IncludeOutstanding: $('#includeoutstanding').is(':checked'),
                ReportTag: $('#reporttag').data('kendoComboBox').value(),
                IncludeUnsubmit: $('#includeunsubmit').is(':checked'),
                DateType: $('#datetype').data('kendoDropDownList').text()
            };
        }
        function error_handler(e) {
            if (e.errors) {
                var message = "Errors:\n";
                $.each(e.errors, function (key, value) {
                    if ('errors' in value) {
                        $.each(value.errors, function () {
                            message += this + "\n";
                        });
                    }
                });
                alert(message);
            }
        }
    function onRejectClick(id) {
                    var comment = document.getElementById("reject.usertext").value;
                    var includeMaster = 0;
                    var followupdate = document.getElementById("reject.duedate").value;
                    var feedbackId = document.getElementById("reject.feedbackId").value;
                    var templateId = document.getElementById("reject_template").value;
                    var upload = $("#files").data("kendoUpload"), files = upload.getFiles();
                    if (files === undefined || files.length == 0) {
                        var file = "";
                    }
                    else {
                        var file = files[0].name;
                    }
                    if (document.getElementById("reject.masterimage").checked) {
                        includeMaster = 1;
                    }

                    if (comment == "") {
                        document.getElementById('reject.message').innerHTML = "Comment is required";
                        document.getElementById('reject.messagediv').style.display = 'block';
                    }
                    else {
                        document.getElementById('reject.messagediv').style.display = 'none';
                        var grid = $("#Grid").data("kendoGrid");
                        var dataItem = grid.dataSource.get(id);
                        dataItem.set("Comment", comment);
                        dataItem.set("FollowUpDate", followupdate);
                        dataItem.set("FeedbackId", feedbackId);
                        dataItem.set("TemplateId", templateId);
                        dataItem.set("RefCompareFile", file);
                        dataItem.set("IncludeMaster", includeMaster);
                        var dialog = $("#Main").data("kendoWindow");
                        dialog.close();
                    }
    }
        </script>

  2. Bill
    Bill avatar
    19 posts
    Member since:
    Jul 2018

    Posted 22 Jul 2019 in reply to Bill Link to this post

    The last sentence of the first paragraph should read:

    On request END call the read action passing in the search criteria(to refresh the grid).

  3. Alex Hajigeorgieva
    Admin
    Alex Hajigeorgieva avatar
    1081 posts

    Posted 24 Jul 2019 Link to this post

    Hello, Bill,

    Thank you very much for the provided code snippets.

    Most of the issues with the data source deeming existing items as new ones is the model id. When the model id is null or it has a default value for its data type, the data source will consider it as new. 

    Also, I wonder how it is possible for the database to create new records if the grid does not support a Create operation at present?

    The Kendo UI Grid data source should not require an additional read to get refreshed - just the expected response which seems to be correct in the Update Action method. 

    I suggest making the following changes:

    - remove the Model from the grid constructor and specify its type instead
    - to pass the additional data to the read method, use the Data() method just like you have for the Update. If you do not want to bind the grid immediately, you can set AutoBind(false) and call the read() method of the data source when desired - the additional data will be passed out of the box as well
    - remove the RequestEnd() event handler
    - ensure the ActivityId is unique 

    @(Html.Kendo().Grid<PortalApp.Areas.TaskManagement.Features.AdvPhotoReview.Index.SearchResult>()
        .Name("Grid")
        .DataSource(dataSource => dataSource
            .Ajax()
            .Events(events => events.Error("error_handler"))
            .Batch(true)
            .Model(model =>
            {
                model.Id(i => i.ActivityId); // Specify the property which is the unique identifier of the model.
                model.Field(i => i.ActivityId).Editable(false); // The Id should not be editable to avoid duplicate ids
            })
            .PageSize(20)
            .Sort(s =>
            {
                s.Add("ProjectId").Ascending();
                s.Add("SubmissionDate").Ascending();
     
            })
            .ServerOperation(false)
            .Update(update => update.Action("Update", "AdvPhotoReview").Data("additionalData"))
            .Read(read => read.Action("Search", "AdvPhotoReview").Data("additionalData"))
        )
        .Editable(editable => editable.Mode(GridEditMode.InCell))
    )
     
    <script>
        function additionalData() {
            var strProjects = getProjects();
            return {
                Store: $('#stores').data('kendoComboBox').value(),
                Type: $('#type').data('kendoComboBox').value(),
                Rep: $('#rep').data('kendoComboBox').value(),
                District: $('#district').data('kendoComboBox').value(),
                DateFrom: $('#datefrom').data('kendoDatePicker').value(),
                DateTo: $('#dateto').data('kendoDatePicker').value(),
                Project: strProjects,
                IncludeOutstanding: $('#includeoutstanding').is(':checked'),
                ReportTag: $('#reporttag').data('kendoComboBox').value(),
                IncludeUnsubmit: $('#includeunsubmit').is(':checked'),
                DateType: $('#datetype').data('kendoDropDownList').text()
            };
        }
    </script>

    Let me know what is the observed behaviour after these changes.

    Regards,
    Alex Hajigeorgieva
    Progress Telerik
    Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
Back to Top