没猜错的话, 你们一定会和一大堆查询条件过不去, 重复的写,反复的写, 写到山崩地裂.
今天看了园友的文字:
实体框架高级应用之动态过滤 EntityFramework DynamicFilters我把我的处理方式给大家分享一下.
查询条件可以是这样的,有很多:
也可以是这样的, 没有几个:
我数了一下,我们系统里的Controller , 112个, 68个查询条件.
如果把这 68个查询条件转换为查询代码的话, 想想都蛋疼啊, 那得写多少啊.
比如这样, 重复,机械,没有营养:
1 public IEnumerable<CREDIT_ASSESS_RESULT> GetCreditResults(CreditAssessResultCondition condition) 2 { 3 using (var db = new Entities()) 4 { 5 IQueryable<CREDIT_ASSESS_RESULT> query = db.CREDIT_ASSESS_RESULT.Where(q => q.STATUS >= 0); 6 if (condition.CARRIER_ID > 0) 7 { 8 query = query.Where(q => q.CARRIER_ID == condition.CARRIER_ID); 9 } 10 11 if (condition.COMPANY_ID > 0) 12 { 13 query = query.Where(q => q.LOCAL_COMPANY_ID == condition.COMPANY_ID); 14 } 15 16 if (condition.ROUTE_ID.HasValue) 17 { 18 query = query.Where(q => q.APPLYTO_ROUTEID == condition.ROUTE_ID); 19 } 20 21 if (condition.CUSTOMER_TYPE.HasValue) 22 { 23 query = query.Where(q => q.APPLYTO_CUSTOMER == condition.CUSTOMER_TYPE); 24 } 25 26 if (condition.CREDIT_PERIOD.HasValue) 27 { 28 //String period = condition.CREDIT_PERIOD.Value.ToString(); 29 query = query.Where(q => q.PERIOD == (int)condition.CREDIT_PERIOD.Value); 30 } 31 32 if (condition.CREDIT_YEAR.HasValue) 33 { 34 query = query.Where(q => q.YEARS.Value == condition.CREDIT_YEAR.Value); 35 } 36 37 if (condition.CREDIT_MONTH > 0) 38 { 39 query = query.Where(q => q.MONTHS == condition.CREDIT_MONTH); 40 } 41 if (condition.CREDIT_SEASON > 0) 42 { 43 query = query.Where(q => q.SEASONS == condition.CREDIT_SEASON); 44 } 45 46 if (!String.IsNullOrWhiteSpace(condition.CUSTOMER_CODE)) 47 { 48 query = query.Where(q => q.COMPANY_ID == condition.CUSTOMER_ID && (q.COMPANY_NAME_CN.ToUpper().Contains(condition.CUSTOMER_CODE.ToUpper()) || 49 q.COMPANY_NAME_EN.ToUpper().Contains(condition.CUSTOMER_CODE.ToUpper()))); 50 } 51 52 if (condition.CustomerSearch.HasValue && condition.CustomerSearch.Value) 53 { 54 query = query.Where(q => q.STATUS > 0); 55 } 56 57 return query.OrderByDescending(q => q.CREATE_DATETIME).ThenBy(q => q.APPLYTO_COMPANY).ThenBy(q => q.COMPANY_NAME_EN).DoPage(condition.Pager).ToList(); 58 } 59 }View Code
为了解放同志们, 我借助 DynamicLinq 写了个简单的解决方案:
1, 先看查询条件的写法:
1 public class RateSearchCondition : BaseQuery<RATES> { 2 3 public decimal? Carrier { 4 get; 5 set; 6 } 7 8 [MapTo("ROUTE_CODE", IgnoreCase = true)] 9 public string Route { 10 get; 11 set; 12 } 13 14 [MapTo("POL_CODE", IgnoreCase = true)] 15 public string Pol { 16 get; 17 set; 18 } 19 20 [MapTo("POD_CODE", IgnoreCase = true)] 21 public string Pod { 22 get; 23 set; 24 } 25 26 /// <summary> 27 /// 启运地 28 /// </summary> 29 [MapTo("FRONT_CODE")] 30 public string OrginCode { 31 get; 32 set; 33 } 34 35 /// <summary> 36 /// 目的地 37 /// </summary> 38 [MapTo("PFD_CODE")] 39 public string DestCode { 40 get; 41 set; 42 } 43 44 [MapTo("EFFECTIVE_DATE")] 45 public DateTime? EffectiveDate { 46 get; 47 set; 48 } 49 50 [MapTo("EXPIRATION_DATE")] 51 public DateTime? ExpirationDate { 52 get; 53 set; 54 } 55 56 public Status? StatusEnum { 57 get; 58 set; 59 } 60 61 [MapTo("STATUS")] 62 public decimal? StatusValue { 63 get { 64 return (decimal?)this.StatusEnum; 65 } 66 } 67 68 [MapTo("SCHEDULE_ID")] 69 public decimal? ScheduleID { 70 get; 71 set; 72 } 73 74 public string Vessel { 75 get; 76 set; 77 } 78 79 public string Voyage { 80 get; 81 set; 82 } 83 84 /// <summary> 85 /// 销售公司 86 /// </summary> 87 public decimal? Company { 88 get; 89 set; 90 } 91 92 public enum Status { 93 [Description("作废")] 94 Invalid = -1, 95 [Description("草稿")] 96 Draft = 0, 97 [Description("已发布")] 98 Published = 1, 99 [Description("历史")] 100 History = 99, 101 } 102 103 public string VesselName { 104 get; 105 set; 106 } 107 108 public bool IncludeHistory { 109 get; 110 set; 111 } 112 113 [MapTo("RATE_NO", IgnoreCase = true)] 114 public string RateNo { 115 get; 116 set; 117 } 118 }
简单的类定义而已, 只不过某些属性上加了 MapToAttribute 特性, 有些没有加.
这些加上了 MapTo 的, 就是本文说的.
2, 看一下查询的写法:
1 public IEnumerable<RATES> Search(RateSearchCondition cond) { 2 using (var db = new Entities()) { 3 var query = cond.Filter(db.RATES.Where(r => !r.DELETE_MARK)); 4 5 6 if (!(cond.IncludeHistory || cond.StatusEnum == RateSearchCondition.Status.History)) { 7 query = query.Where(m => m.STATUS != (int)RateSearchCondition.Status.History) 8 .OrderByDescending(r => r.MODIFY_DATETIME); 9 } else 10 query = query.OrderByDescending(r => r.CREATEBY ?? 0); 11 12 return query 13 .DoPage(cond.Pager) 14 .ToList(); 15 } 16 }
这样看, 代码是不是清爽很多?
那么多查询条件, 基本都浓缩进了 cond.Filter(...) 中了, 如果要加其它的查询条件, 只需要改一下查询条件的类, 并加上 MapTo 就可以了.
不能浓缩进去的, 直接写在代码里, 反正也没有几个.
3, 看一下 MapTo 的定义:
1 /// <summary> 2 /// 用于 BaseQuery 3 /// </summary> 4 [AttributeUsage(AttributeTargets.Property)] 5 public class MapToAttribute : Attribute { 6 7 8 /// <summary> 9 /// 映射到的属性 10 /// </summary> 11 public string Field { 12 get; 13 set; 14 } 15 16 /// <summary> 17 /// 比较类型 18 /// </summary> 19 public MapToOpts Opt { 20 get; 21 set; 22 } 23 24 /// <summary> 25 /// 是否忽略大小写 26 /// </summary> 27 public bool IgnoreCase { 28 get; 29 set; 30 } 31 32 /// <summary> 33 /// 34 /// </summary> 35 /// <param name="field"></param> 36 public MapToAttribute(string field) { 37 if (string.IsNullOrEmpty(field)) 38 throw new ArgumentNullException("field"); 39 40 this.Field = field; 41 } 42 43 } 44 45 /// <summary> 46 /// 比较类型 47 /// </summary> 48 public enum MapToOpts { 49 /// <summary> 50 /// 等于 51 /// </summary> 52 Equal, 53 54 /// <summary> 55 /// 不等于 56 /// </summary> 57 NotEqual, 58 59 /// <summary> 60 /// 大于 61 /// </summary> 62 Gt, 63 64 /// <summary> 65 /// 大于等于 66 /// </summary> 67 GtOrEqual, 68 69 /// <summary> 70 /// 小于 71 /// </summary> 72 Lt, 73 74 /// <summary> 75 /// 小于等 76 /// </summary> 77 LtOrEqual, 78 79 /// <summary> 80 /// 以 XXX 开始(字符串) 81 /// </summary> 82 StartWith, 83 84 /// <summary> 85 /// 以XXX结尾(字符串) 86 /// </summary> 87 EndWith, 88 89 /// <summary> 90 /// 包含XXX(字符串) 91 /// </summary> 92 Include 93 }
从 MapToOpts 可以看出, 基本的大于,小于,等于,不等于, 开始,包含,结束这样的查询条件都可以涵盖到.
但是像 是否包含在集合中 / And / Or 操作是不支持的, 它们需要写在查询方法里, 所以标题我就写的是: 基本数据过滤
4, BaseQuery 的定义:
1 [Serializable] 2 public class BaseQuery { 3 4 private Pager pager = null; 5 public Pager Pager { 6 get { 7 if (this.pager == null) 8 this.pager = new Pager(); 9 return this.pager; 10 } 11 set { 12 this.pager = value; 13 } 14 } 15 16 private bool allowPage = true; 17 /// <summary> 18 /// 是否分页 19 /// </summary> 20 [DisableBinding] 21 public bool AllowPage { 22 get { 23 return this.allowPage; 24 } 25 set { 26 this.allowPage = value; 27 } 28 } 29 } 30 31 [Serializable] 32 public class BaseQuery<T> : BaseQuery where T : class { 33 34 public IQueryable<T> Filter(IQueryable<T> source) { 35 var ps = this.GetType() 36 .GetProperties() 37 .ToList(); 38 39 ps.ForEach(p => { 40 var mapTo = p.GetCustomAttribute<MapToAttribute>(); 41 if (mapTo != null) { 42 var st = typeof(T).GetProperty(mapTo.Field); 43 if (st != null) { 44 var opt = string.Empty; 45 switch (mapTo.Opt) { 46 case MapToOpts.Equal: 47 opt = "=="; 48 break; 49 case MapToOpts.NotEqual: 50 opt = "!="; 51 break; 52 case MapToOpts.Gt: 53 opt = ">"; 54 break; 55 case MapToOpts.Lt: 56 opt = "<"; 57 break; 58 case MapToOpts.GtOrEqual: 59 opt = ">="; 60 break; 61 case MapToOpts.LtOrEqual: 62 opt = "<="; 63 break; 64 } 65 66 if (!string.IsNullOrEmpty(opt)) { 67 var v = p.GetValue(this); 68 if (v != null) { 69 string cond = ""; 70 if (v.GetType() == typeof(string) && mapTo.Opt == MapToOpts.Equal && mapTo.IgnoreCase) { 71 if (!string.IsNullOrWhiteSpace(v.ToString())) 72 { 73 cond = string.Format("{0}.ToUpper() {1} @0", st.Name, opt); 74 source = source.Where(cond, ((string)v).ToUpper()); 75 } 76 } else { 77 cond = string.Format("{0} {1} @0", st.Name, opt); 78 source = source.Where(cond, v); 79 } 80 81 82 } 83 } else { 84 var cond = string.Empty; 85 86 var v = (string)p.GetValue(this); 87 if (v == null || string.IsNullOrEmpty((string)v)) 88 return; 89 90 v = v.Replace("/"", ""); 91 92 if (string.IsNullOrEmpty(v)) 93 return; 94 95 var ignoreCaseStr = mapTo.IgnoreCase ? ".ToUpper()" : ""; 96 if (mapTo.IgnoreCase) 97 v = v.ToUpper(); 98 99 switch (mapTo.Opt) { 100 case MapToOpts.Include: 101 cond = string.Format("{0}{1}.IndexOf(/"{2}/") != -1", st.Name, ignoreCaseStr, v); 102 break; 103 case MapToOpts.StartWith: 104 cond = string.Format("{0}{1}.StartsWith(/"{2}/")", st.Name, ignoreCaseStr, v); 105 break; 106 case MapToOpts.EndWith: 107 cond = string.Format("{0}{1}.EndsWith(/"{2}/")", st.Name, ignoreCaseStr, v); 108 break; 109 } 110 111 if (!string.IsNullOrEmpty(cond)) { 112 source = source.Where(cond); 113 } 114 115 } 116 } 117 } 118 }); 119 120 return source; 121 } 122 }
Filter 方法先获取泛型参数的所有 Property, 然后遍例, 获取加到 Property 上的 MapToAttribute 进行相关的操作.
如果某个条件的值是 null , 说明这个条件跳过.
如果是字符类型的条件, 会跟据是否 IgnoreCase 应用 ToUpper , 因为我们用的是 ORACLE 数据, 大小写敏感. 还会应用 IndexOf, StartsWith, EndsWidth
----------------------------
好啦 , 就这样.