Skip to content

Instantly share code, notes, and snippets.

@MatthewBarker
Created February 18, 2015 15:54
Show Gist options
  • Save MatthewBarker/38cef4757fe77bcd2b1a to your computer and use it in GitHub Desktop.
Save MatthewBarker/38cef4757fe77bcd2b1a to your computer and use it in GitHub Desktop.
Server side sorting, paging & filtering from DataTable to Kendo UI Grid
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using Newtonsoft.Json;
/// <summary>
/// Represents the data service.
/// </summary>
public class DataService
{
/// <summary>
/// Gets the grid.
/// </summary>
/// <param name="take">The number of records to take</param>
/// <param name="skip">The number of records to skip</param>
/// <param name="sort">The sort to apply</param>
/// <param name="filter">The filter to apply</param>
/// <returns>
/// The grid.
/// </returns>
public Grid GetGrid(
int take,
int skip,
IEnumerable<Sort> sort,
Filter filter)
{
var source = new DataTable(); // Get the data here
var sortExpression = sort == null ? string.Empty : string.Join(",", sort.Select(item => item.GetExpression()));
var filterExpression = filter == null ? string.Empty : filter.GetExpression();
IEnumerable<DataRow> filtered = source.Select(filterExpression, sortExpression);
IEnumerable<DataRow> page = filtered.Skip(skip).Take(take);
DataTable destination = source.Clone();
page.ToList().ForEach(row => destination.ImportRow(row));
var grid = new Grid()
{
Data = JsonConvert.SerializeObject(destination),
Total = filtered.Count()
};
return grid;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
/// <summary>
/// Represents a filter.
/// </summary>
[DataContract, Serializable]
public class Filter
{
/// <summary>
/// The templates
/// </summary>
private static readonly IDictionary<string, string> Templates = new Dictionary<string, string>
{
{ "eq", "{0} = '{1}'" },
{ "neq", "{0} <> '{1}'" },
{ "lt", "{0} < '{1}'" },
{ "lte", "{0} <= '{1}'" },
{ "gt", "{0} > '{1}'" },
{ "gte", "{0} >= '{1}'" },
{ "startswith", "{0} like '{1}*'" },
{ "endswith", "{0} like '*{1}'" },
{ "contains", "{0} like '*{1}*'" },
{ "doesnotcontain", "{0} not like '*{1}*'" }
};
/// <summary>
/// Gets or sets the field.
/// </summary>
/// <value>
/// The field.
/// </value>
[DataMember(Name = "field")]
public string Field { get; set; }
/// <summary>
/// Gets or sets the filters.
/// </summary>
/// <value>
/// The filters.
/// </value>
[DataMember(Name = "filters")]
public IEnumerable<Filter> Filters { get; set; }
/// <summary>
/// Gets or sets the logic.
/// </summary>
/// <value>
/// The logic.
/// </value>
[DataMember(Name = "logic")]
public string Logic { get; set; }
/// <summary>
/// Gets or sets the operator.
/// </summary>
/// <value>
/// The operator.
/// </value>
[DataMember(Name = "operator")]
public string Operator { get; set; }
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>
/// The value.
/// </value>
[DataMember(Name = "value")]
public object Value { get; set; }
/// <summary>
/// Gets the expression.
/// </summary>
/// <returns>
/// The expression.
/// </returns>
public string GetExpression()
{
return this.GetExpression(this.Filters, this.Logic);
}
/// <summary>
/// Called when deserialized.
/// </summary>
/// <param name="context">The context.</param>
[OnDeserialized]
public void OnDeserialized(StreamingContext context)
{
if (this.Value != null)
{
var value = this.Value.ToString();
// DateTime values are sent in the format /Date(0000000000000)/
if (value.Substring(0, 6) == "/Date(" && value.Length > 20)
{
// The digits represent the milliseconds since the start of the Unix epoch
var milliseconds = long.Parse(value.Substring(6, 13));
var unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
// This date format works with the data table select statement
this.Value = unixEpoch.AddMilliseconds(milliseconds).ToString("yyyy-MM-dd");
}
}
}
/// <summary>
/// Gets the expression.
/// </summary>
/// <param name="filters">The filters.</param>
/// <param name="logic">The logic.</param>
/// <returns>
/// The expression.
/// </returns>
private string GetExpression(IEnumerable<Filter> filters, string logic)
{
string result = string.Empty;
if (filters != null && filters.Any<Filter>() && !string.IsNullOrWhiteSpace(logic))
{
var list = new List<string>();
foreach (Filter filter in filters)
{
if (!string.IsNullOrWhiteSpace(filter.Field))
{
string template = Templates[filter.Operator];
string value = filter.Value.ToString();
list.Add(string.Format(template, filter.Field, value));
}
if (filter.Filters != null)
{
list.Add(this.GetExpression(filter.Filters, filter.Logic));
}
}
result = "(" + string.Join(" " + logic + " ", list) + ")";
}
return result;
}
}
using System;
using System.Runtime.Serialization;
/// <summary>
/// Represents the grid.
/// </summary>
[DataContract, Serializable]
public class Grid
{
/// <summary>
/// Gets or sets the data.
/// </summary>
/// <value>
/// The data.
/// </value>
[DataMember(Name = "data")]
public string Data { get; set; }
/// <summary>
/// Gets or sets the total.
/// </summary>
/// <value>
/// The total.
/// </value>
[DataMember(Name = "total")]
public int Total { get; set; }
}
using System;
using System.Runtime.Serialization;
/// <summary>
/// Represents a sort.
/// </summary>
[DataContract, Serializable]
public class Sort
{
/// <summary>
/// Gets or sets the direction.
/// </summary>
/// <value>
/// The direction.
/// </value>
[DataMember(Name = "dir")]
public string Direction { get; set; }
/// <summary>
/// Gets or sets the field.
/// </summary>
/// <value>
/// The field.
/// </value>
[DataMember(Name = "field")]
public string Field { get; set; }
/// <summary>
/// Gets the expression.
/// </summary>
/// <returns>
/// The expression.
/// </returns>
public string GetExpression()
{
return this.Field + " " + this.Direction;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment