Wednesday 28 December 2011

Controlling Multiple Form Posts in Telerik Grid For MVC

At my work place we are developing a web based application that is very data centric and involves a lot of Grids. After a lot of Grid survey we ended up using the Telerik Grids for MVC. The problem with the grid was that when it was used  with ajax binding and the edit mode set to GridEditMode.PopUp, of the user submits the form twice the value was inserted in the Database multiple times. Click here to enlarge the image




So to filter out the identical form posting i wrote a helper method to generate a GUID

namespace Namespace.FilterAttributes
{

public static class GuidHelper{

  public static MvcHtmlString GetGuid(this HtmlHelper helper)

   {

    var guid = Guid.NewGuid().ToString();

    helper.ViewContext.HttpContext.Session[guid] = "";

    return MvcHtmlString.Create(string.Form  at("<input type='hidden' id='FormGuid' name='FormGuid' value='{0}' />", guid));

 } 


}
in the view to generate the GUID simply
<%@ Import Namespace="Namespace.FilterAttributes" %>

<%:Html.GetGuid()%>

attach a OnSave Client event to the grid so the GUID generate can be transported to the server
function onSave(e) {                 
          e.values["FormGuid"] = $("input[name='FormGuid']").val();
      }

in the ActionFilter attribute look in the session for the GUID if the session is null or empty put the value in the session so that the next time if the same form is posted again it should be filtered out.
public class MultiFormControlAttribute : ActionFilterAttribute   {

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var ctx = filterContext.HttpContext;
            var response = ctx.Response;       
            var session = ctx.Session;
            var fGuid = ctx.Request.Form["FormGuid"];
            if (ctx.Session["fGuid"] != null)
            {
                if (fGuid.Equals(ctx.Session["fGuid"].ToString()))
                {
                    //kill the request kill it!!!       

                   //add the model error 
                    filterContext.Controller.ViewData.ModelState.AddModelError("", "Duplicate form post");
                }
                ctx.Session["fGuid"] = fGuid;
            }
            else
                ctx.Session["fGuid"] = fGuid;            
        }
    }

Decorate the ActionResult responsible for the insertion of the values in the Grid with the MultiFormControl Attribute.
        [GridAction,MultiFormControl]

        public ActionResult _AjaxBindingInsert(User _User) 
        {

             if(ModelState.IsValid){
            _users.Add(_User);
            Session[_user_key] = _users;

         }
            return View(new GridModel(_users));
        }

So far so good but the problem arises after the first value has been inserted upon inserting the second value the GUID was the same old one that was generated for the previous form it was not updated as the form was posted via AJAX. So to refresh the GUID upon submission of the form attach a OnDataBound client event to the Grid and reset the GUID
function OnDataBound(e) {

 $.post('<%= Url.Action("GetGuid") %>', function (data) {

 $("input[name='FormGuid']").val(data);
 });
}

Add an overloaded helper to the GuidHelper class to generate the GUID OnDataBound
  public static string GetGuid(HttpContextBase context)
{

  var guid = Guid.NewGuid().ToString();
           context.Session[guid] = "";// Not null
           return guid;
  }

and an ActionResult by the name of GetGuid
public ActionResult GetGuid()
{

return Content(GuidHelper.GetGuid(HttpContext));
        }

Now upon each successful submission of the form the GUID is regenerate at the same time identical form submissions via ajax are filtered out.

The complete demo can be found here




1 comment: