I am using jqGrid in my current project, and I already blogged prior on how impressed I am with the functionality available in this control. I am using it in ASP.NET MVC application, but it can be used in any web app. One of the key features is that it supports ajax based asynchronous server operations.
Upon my research I stumbled onto this article that greatly simplifies the code required to process search requests in ASP.NET MVS that are generated by the grid
http://www.codeproject.com/KB/aspnet/AspNetMVCandJqGrid.aspx
I had to make a small addition to it though. The code works great in multi-search scenarios, but if you turn off multisearch option in search options, than you will get an error. I also remove empty try/catch blocks which I personally find to be a bad practice. Here is the updated code I used for Filter class:
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
[DataContract]
public class Filter
{
[DataMember]
public string groupOp { get; set; }
[DataMember]
public Rule[] rules { get; set; }
public static Filter Create(
string jsonData,
string searchField,
string searchString,
string searchOper)
{
Filter returnValue = null;
if (!string.IsNullOrEmpty(jsonData))
{
var serializer = new DataContractJsonSerializer(typeof(Filter));
using (var ms = new System.IO.MemoryStream(Encoding.Default.GetBytes(jsonData)))
{
returnValue = serializer.ReadObject(ms) as Filter;
}
}
else
{
returnValue =
new Filter()
{
groupOp = "AND",
rules = new[] { new Rule()
{ data = searchString, field = searchField, op = searchOper } }
};
}
return returnValue;
}
}
I added else statement that checks the json data variable, which will be null in cases of single field search. The code will fall into same patch though, which is convenient because you process data code can stay the same whether or not you are using single or multi-search.
Of course, I had to update the model binder to match:
using System.Web.Mvc;
public class GridModelBinder : IModelBinder
{
public object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
var request = controllerContext.HttpContext.Request;
return new GridSettings
{
IsSearch = bool.Parse(request["_search"] ?? "false"),
PageIndex = int.Parse(request["page"] ?? "1"),
PageSize = int.Parse(request["rows"] ?? "10"),
SortColumn = request["sidx"] ?? "",
SortOrder = request["sord"] ?? "asc",
Where = Filter.Create(
request["filters"],
request["searchField"],
request["searchString"],
request["searchOper"])
};
}
}
I want to thank Ilya Builuk for sharing his code. I hope you will find my enhancements useful. Another side thought. There is a string based IQueryable extensions library that you can use to construct queries against this interface based on syntax such as “Where SomeColumn =- 2”. Not that I am a giant fan of leaking my data access code into controllers or anything. You can read more about dynamic linq on Scott Guthrie’s blog here
Thanks.
Hi,
Great article! Helped me a lot and directed in right direction. However I am struggling in figuring it out as How can I implement the same in multi tier application with repository pattern.
I do not want to implement following repetitive code in each controller action.
var query = _repository.Computers();
//filtring
if (grid.IsSearch)
{
//And
if (grid.Where.groupOp == “AND”)
foreach (var rule in grid.Where.rules)
query = query.Where(
rule.field, rule.data,
(WhereOperation)StringEnum.Parse(typeof(WhereOperation), rule.op));
else
{
//Or
var temp = (new List()).AsQueryable();
foreach (var rule in grid.Where.rules)
{
var t = query.Where(
rule.field, rule.data,
(WhereOperation)StringEnum.Parse(typeof(WhereOperation), rule.op));
temp = temp.Concat(t);
}
//remove repeating records
query = temp.Distinct();
}
}
//sorting
query = query.OrderBy(grid.SortColumn,
grid.SortOrder);
//count
var count = query.Count();
//paging
var data = query.Skip((grid.PageIndex – 1) * grid.PageSize).Take(grid.PageSize).ToArray();
Rather want a repository method to handle this code. Also I want to implement pagination.
Any suggestions and help on this is highly appreciable.
Thanks.
You should probably create a base repository and put that the code there. Assuming you use EF you could have something like
public PagedData GetPagedData (IDictionary filters, string sortColumns, string sortDirection, int pageNumber, int pageSize) ().OrderBy(sortColumn, sortOrder).Skip().Take()….
{
var query = context.Set
}
then you have a class for result
public class PagedData Data{get; internal set;};
{
public Ienumerable
public int Total{}
public int Rows{}
}
etc…
Hope this is what you are looking for.
Sergey Barskiy