PageRenderTime 25ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/MVC.Controls/SearchModel.cs

#
C# | 285 lines | 221 code | 52 blank | 12 comment | 21 complexity | 927fc48cc731a1e017f9509f85b84586 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Web.Mvc;
  6. using System.Web.Script.Serialization;
  7. using System.Collections;
  8. using System.Linq.Dynamic;
  9. using System.Reflection;
  10. namespace MVC.Controls
  11. {
  12. internal class SearchScriptSerializer : JavaScriptConverter
  13. {
  14. public override IEnumerable<Type> SupportedTypes
  15. {
  16. get { return new List<Type>() { typeof(SearchModel) }; }
  17. }
  18. public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
  19. {
  20. string val;
  21. //groupOp
  22. SearchModel res = new SearchModel();
  23. val = (string)dictionary["groupOp"];
  24. if (val == "AND")
  25. res.ConjunctionOp = ConjunctionOperator.AND;
  26. else
  27. res.ConjunctionOp = ConjunctionOperator.OR;
  28. if (dictionary["rules"] != null)
  29. {
  30. res.Filters = new List<FilterModel>();
  31. IEnumerable rulesList = dictionary["rules"] as IEnumerable;
  32. foreach (IDictionary<string, object> rawRule in rulesList)
  33. {
  34. FilterModel f = new FilterModel();
  35. f.Value = (string)rawRule["data"];
  36. f.SetOp((string)rawRule["op"]);
  37. f.ColumnName = (string)rawRule["field"];
  38. res.Filters.Add(f);
  39. }
  40. }
  41. return res;
  42. }
  43. public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
  44. {
  45. throw new NotImplementedException();
  46. }
  47. }
  48. public class SearchModelBinder : DefaultModelBinder
  49. {
  50. public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
  51. {
  52. SearchModel model = new SearchModel();
  53. bool search = false;
  54. search = (bool)bindingContext.ValueProvider.GetValue("_search").ConvertTo(typeof(Boolean));
  55. if (search)
  56. {
  57. ValueProviderResult filters = bindingContext.ValueProvider.GetValue("filters");
  58. if (filters != null)
  59. {
  60. JavaScriptSerializer serializer = new JavaScriptSerializer();
  61. serializer.RegisterConverters(new JavaScriptConverter[] { new SearchScriptSerializer() });
  62. model = serializer.Deserialize<SearchModel>((string)filters.ConvertTo(typeof(String)));
  63. }
  64. else
  65. {
  66. FilterModel filter = new FilterModel();
  67. filter.ColumnName = (string)bindingContext.ValueProvider.GetValue("searchField").ConvertTo(typeof(String));
  68. filter.Value = (string)bindingContext.ValueProvider.GetValue("searchString").ConvertTo(typeof(String));
  69. filter.SetOp((string)bindingContext.ValueProvider.GetValue("searchOper").ConvertTo(typeof(String)));
  70. model.Filters.Add(filter);
  71. }
  72. }
  73. model.Page = (int)bindingContext.ValueProvider.GetValue("page").ConvertTo(typeof(Int32));
  74. model.Rows = (int)bindingContext.ValueProvider.GetValue("rows").ConvertTo(typeof(Int32));
  75. model.SortColumnName = (string)bindingContext.ValueProvider.GetValue("sidx").ConvertTo(typeof(String));
  76. model.SortOrder = (string)bindingContext.ValueProvider.GetValue("sord").ConvertTo(typeof(string));
  77. //model.PageIndex = (int)bindingContext.ValueProvider.GetValue("page").ConvertTo(typeof(Int32)) - 1;
  78. //model.RecordsCount = (int)bindingContext.ValueProvider.GetValue("rows").ConvertTo(typeof(Int32));
  79. return model;
  80. }
  81. }
  82. public enum ConjunctionOperator
  83. {
  84. AND,
  85. OR
  86. }
  87. [ModelBinder(typeof(SearchModelBinder))]
  88. public class SearchModel
  89. {
  90. public ConjunctionOperator ConjunctionOp { get; set; }
  91. public List<FilterModel> Filters { get; set; }
  92. public SearchModel()
  93. {
  94. this.Filters = new List<FilterModel>();
  95. }
  96. public bool IsSearch
  97. {
  98. get
  99. {
  100. return this.Filters.Count > 0;
  101. }
  102. }
  103. public int Page { get; set; }
  104. public int Rows { get; set; }
  105. public string SortColumnName { get; set; }
  106. public string SortOrder { get; set; }
  107. /// <summary>
  108. /// Applies the filters and orderings from the grid to the IQueryable datasource
  109. /// </summary>
  110. /// <typeparam name="T"></typeparam>
  111. /// <param name="modelQuery"></param>
  112. /// <returns></returns>
  113. public IQueryable<T> ApplyFilters<T>(IQueryable<T> modelQuery)
  114. {
  115. IQueryable<T> res = modelQuery;
  116. if (this.Filters.Count > 0)
  117. {
  118. string temp = this.FiltersToString(typeof(T));
  119. res = modelQuery.Where(temp);
  120. }
  121. if (!string.IsNullOrEmpty(this.SortColumnName))
  122. res = res.OrderBy(this.SortColumnName + " " + this.SortOrder);
  123. return res;
  124. }
  125. public string FiltersToString(Type entityType)
  126. {
  127. if (this.Filters == null) return "";
  128. string conOp = " && ";
  129. if (ConjunctionOp == ConjunctionOperator.OR) conOp = " || ";
  130. StringBuilder sb = new StringBuilder();
  131. /* Enuerate through all the filters to generate a complete Where statement */
  132. foreach (FilterModel f in this.Filters)
  133. {
  134. /* Since a column could be bound to a child property (e.g City.Id), the property discovery is recursive */
  135. IEnumerable<PropertyInfo> query;
  136. Type columnType = entityType;
  137. string[] propertyHeirarch = f.ColumnName.Split('.');
  138. foreach (string prop in propertyHeirarch)
  139. {
  140. PropertyInfo[] props = columnType.GetProperties();
  141. query = from p in props
  142. where p.Name == prop
  143. select p;
  144. columnType = query.First().PropertyType;
  145. }
  146. sb.Append(conOp + f.ToString(columnType));
  147. }
  148. if (sb.Length > 0) sb.Remove(0, 4);
  149. return sb.ToString();
  150. }
  151. }
  152. public class FilterModel
  153. {
  154. public string ColumnName { get; set; }
  155. public FilterOp Op { get; set; }
  156. public string Value { get; set; }
  157. public string ToString(Type columnType)
  158. {
  159. string newValue;
  160. string columnPrefix = "";
  161. if (isNumericType(columnType))
  162. newValue = this.Value;
  163. else
  164. {
  165. if (columnType != typeof(string))
  166. columnPrefix = ".ToString()";
  167. newValue = "\"" + this.Value + "\"";
  168. }
  169. //TODO: Check type
  170. switch (this.Op)
  171. {
  172. case FilterOp.BeginWith: return this.ColumnName + columnPrefix + ".StartsWith(" + newValue + ")";
  173. case FilterOp.Contain: return this.ColumnName + columnPrefix + ".Contains(" + newValue + ")";
  174. case FilterOp.EndWith: return this.ColumnName + columnPrefix + ".EndsWith(" + newValue + ")";
  175. case FilterOp.Equal: return this.ColumnName + columnPrefix + "==" + newValue;
  176. case FilterOp.NotContain: return this.ColumnName + columnPrefix + ".Contains(" + newValue + ")==false";
  177. case FilterOp.Greater: return this.ColumnName + ">" + newValue;
  178. case FilterOp.GreaterOrEqual: return this.ColumnName + ">=" + newValue;
  179. case FilterOp.In: return newValue + ".Contains(" + this.ColumnName + columnPrefix + ")"; //TODO: Test
  180. case FilterOp.Less: return this.ColumnName + "<" + newValue;
  181. case FilterOp.LessOrEqual: return this.ColumnName + "<=" + newValue;
  182. case FilterOp.NotBeginWith: return this.ColumnName + columnPrefix + ".StartWith(" + newValue + ")==false";
  183. case FilterOp.NotIn: return newValue + ".Contains(" + this.ColumnName + columnPrefix + ")==false"; //TODO: Test
  184. case FilterOp.NotEndWith: return this.ColumnName + columnPrefix + ".EndsWith(" + newValue + ")==false";
  185. case FilterOp.NotEqual: return this.ColumnName + columnPrefix + "!=" + newValue;
  186. default: return "";
  187. }
  188. }
  189. public void SetOp(string opName)
  190. {
  191. switch (opName)
  192. {
  193. case "eq": this.Op = FilterOp.Equal; break;
  194. case "ne": this.Op = FilterOp.NotEqual; break;
  195. case "lt": this.Op = FilterOp.Less; break;
  196. case "le": this.Op = FilterOp.LessOrEqual; break;
  197. case "gt": this.Op = FilterOp.Greater; break;
  198. case "ge": this.Op = FilterOp.GreaterOrEqual; break;
  199. case "bw": this.Op = FilterOp.BeginWith; break;
  200. case "bn": this.Op = FilterOp.NotBeginWith; break;
  201. case "in": this.Op = FilterOp.In; break;
  202. case "ni": this.Op = FilterOp.NotIn; break;
  203. case "ew": this.Op = FilterOp.EndWith; break;
  204. case "en": this.Op = FilterOp.NotEndWith; break;
  205. case "cn": this.Op = FilterOp.Contain; break;
  206. case "nc": this.Op = FilterOp.NotContain; break;
  207. default: throw new Exception("FilterOp " + opName + " Unknown");
  208. }
  209. }
  210. private bool isNumericType(Type t)
  211. {
  212. string name = t.Name;
  213. switch (name)
  214. {
  215. case "Int16": return true;
  216. case "Int32": return true;
  217. case "Int64": return true;
  218. case "UInt16": return true;
  219. case "UInt32": return true;
  220. case "UInt64": return true;
  221. case "Double": return true;
  222. case "Single": return true;
  223. }
  224. return false;
  225. }
  226. }
  227. public enum FilterOp
  228. {
  229. Equal, NotEqual, Less, LessOrEqual, Greater, GreaterOrEqual,
  230. BeginWith, NotBeginWith, In, NotIn, EndWith, NotEndWith,
  231. Contain, NotContain
  232. }
  233. }