在進行數據列表的查詢中,我們通常會使用兩種方式進行查詢:
- linq查詢
- 數據庫sql語句查詢
這樣固然可以實現查詢,本人之前也都是這么做的,因為查詢的條件很少。使用linq,可以將所有的查詢條件的屬性傳到后台,再根據該屬性是否有值,使用where進行查詢;使用存儲過程,也需要將所有查詢條件的屬性傳到后台,
再根據該屬性是否有值進行sql語句的拼接。這樣做在查詢條件很少的時候固然沒啥影響,但是有一天做查詢列表的時候,本人碰到了一個查詢條件高達接近10個的情況,這樣再使用上述的方法固然也可以實現,但是可能會使用多個
條件去判斷,然后使用多個where查詢,如果在數據庫使用sql語句拼接,可能會產生很長的sql拼接查詢的代碼,在調試的時候還要用print去打印才能看到完整的查詢條件,所以我就想,能不能寫一種方法可以動態產生查詢的條件
,不管列表的查詢條件有十個還是幾十個,我只需要將查詢的屬性作為一個對象傳到后台,再將這個查詢屬性的對象丟給這個方法,讓它動態產生查詢的表達式,然后使用linq中的where去查詢呢?於是我便踏上了探索Expression表
達式樹的路途!下面我寫出自己的代碼,希望和大家能一起交流學習!
經過查詢資料和研究,終於寫出了以下這套代碼來達到目的:
1.定義一個查詢的特性類,_displayName用於指明查詢條件的類的各個屬性用於和數據源列表元素對象的各個屬性比較的屬性名稱(注意:_displayName的值必須和數據源中各個屬性名稱相同),_operation用於指明比較的操作
類型,類的代碼如下:
1 public class SearchAttribute : Attribute 2 { 3 /// <summary> 4 /// 屬性名稱,用於對比查詢 5 /// </summary> 6 private string _displayName; 7 /// <summary> 8 /// 操作類型 9 /// </summary> 10 private OperationType _operation; 11 12 public string DisplayName 13 { 14 get { return _displayName; } 15 } 16 public OperationType Operation 17 { 18 get { return _operation; } 19 } 20 21 public SearchAttribute(string displayName, OperationType operation) 22 { 23 _displayName = displayName; 24 _operation = operation; 25 } 26 27 /// <summary> 28 /// 不是查詢的條件時調用此構造函數 參數值=OperationType.None 29 /// </summary> 30 /// <param name="operation"></param> 31 public SearchAttribute(OperationType operation) 32 { 33 _operation = operation; 34 } 35 }
2.定義一個比較的操作類型的枚舉類型
1 /// <summary> 2 /// 查詢操作類型 3 /// </summary> 4 public enum OperationType 5 { 6 /// <summary> 7 /// 不進行查詢 8 /// </summary> 9 None, 10 /// <summary> 11 /// 比較該查詢屬性的值是否與元數據數據的值相等 即sql中= 12 /// </summary> 13 Equal, 14 /// <summary> 15 /// 比較元數據數據的值是否包含該查詢屬性的值 即sql中like 16 /// </summary> 17 Like, 18 /// <summary> 19 /// 大於 20 /// </summary> 21 GreaterThan, 22 /// <summary> 23 /// 小於 24 /// </summary> 25 LessThan, 26 /// <summary> 27 /// >= 28 /// </summary> 29 GreaterThanOrEqual, 30 /// <summary> 31 /// <= 32 /// </summary> 33 LessThanOrEqual 34 }
3.核心代碼來了,哈哈
1 public static class Query 2 { 3 public static IQueryable<TSource> Search<TSource,T>(this IQueryable<TSource> queryList, T searchOptions) 4 { 5 return queryList.Where(Search<TSource,T>(searchOptions)); 6 } 7 8 private static Expression<Func<TSource, bool>> Search<TSource,T>(T searchOptionEntity) 9 { 10 var dataSouceType = typeof(TSource); //數據源列表元素對象的類型 11 var dataSource = new 12 { 13 Type = dataSouceType, //數據源列表元素對象的類型 14 Properties = dataSouceType.GetProperties(), //數據源列表元素對象的屬性集合 15 }; 16 17 //List<string> sourcePropertyName = sourceProperties.Select(p => p.Name).ToList(); 18 19 PropertyInfo[] searchProperties = searchOptionEntity.GetType().GetProperties(); //查詢選擇器對象的屬性集合 20 21 var pe = Expression.Parameter(dataSource.Type, "p"); //創建一個 ParameterExpression 節點,該節點可用於標識表達式樹中的參數或變量 22 var expression = Expression.Equal(Expression.Constant(true), Expression.Constant(true)); 23 24 //遍歷查詢選擇器對象的屬性集合 25 foreach (var property in searchProperties) 26 { 27 var propertySearchAttribute = property.GetCustomAttributes(true)[0] as SearchAttribute; //獲取查詢選擇器屬性的自定義特性對象 28 var propertySearchVlaue = property.GetValue(searchOptionEntity, null); //獲取查詢選擇器屬性的值 29 var propertySearchAttributeName = propertySearchAttribute.DisplayName; //獲取查詢選擇器屬性的自定義特性對象的對比查詢的字段名稱 30 31 //查詢選擇器中的該屬性的自定義的對比查詢的字段名稱 in 數據源列表對象的屬性集合 && 查詢選擇器中的該屬性是查詢的條件 && 查詢選擇器該屬性的值!=null或者"" 32 if (Array.Exists(dataSource.Properties, p => p.Name == propertySearchAttributeName) && propertySearchAttribute.Operation!=OperationType.None && propertySearchVlaue != null && propertySearchVlaue != (object)string.Empty) 33 { 34 var propertyReference = Expression.Property(pe, propertySearchAttributeName); 35 var sourcePropertyType = dataSource.Properties.FirstOrDefault(p => p.Name == propertySearchAttributeName).PropertyType; //獲取數據源列表元素對象的單個屬性的屬性類型 36 ConstantExpression constantReference = null; 37 Expression Expr = null; 38 39 bool isGenericType = sourcePropertyType.IsGenericType && sourcePropertyType.GetGenericTypeDefinition() == typeof(Nullable<>); //搜索sourcePropertyType是否可為空 40 if (isGenericType) 41 constantReference = Expression.Constant(Convert.ChangeType(propertySearchVlaue, Nullable.GetUnderlyingType(sourcePropertyType)), sourcePropertyType); //如果可為空類型,則將propertySearchVlaue的類型設置為可為空類型 42 else 43 constantReference = Expression.Constant(Convert.ChangeType(propertySearchVlaue, sourcePropertyType)); 44 45 //根據查詢選擇器中該屬性的查詢條件進行不同的操作 46 switch (propertySearchAttribute.Operation) 47 { 48 case OperationType.Equal: 49 Expr = Expression.Equal(propertyReference, constantReference); 50 break; 51 case OperationType.GreaterThan: 52 Expr = Expression.GreaterThan(propertyReference, constantReference); 53 break; 54 case OperationType.LessThan: 55 Expr = Expression.LessThan(propertyReference, constantReference); 56 break; 57 case OperationType.GreaterThanOrEqual: 58 Expr = Expression.GreaterThanOrEqual(propertyReference, constantReference); 59 break; 60 case OperationType.LessThanOrEqual: 61 Expr = Expression.LessThanOrEqual(propertyReference, constantReference); 62 break; 63 case OperationType.Like: 64 Expr = Expression.Call(propertyReference, typeof(String).GetMethod("Contains", new Type[] { typeof(string) }), constantReference); 65 break; 66 default:break; 67 68 } 69 70 expression = Expression.AndAlso(expression, Expr); //最終的查詢條件 71 } 72 } 73 return Expression.Lambda<Func<TSource, bool>>(expression, pe); 74 } 75 }
注意:必須將Query類和該類的成員方法定義為static,具體原因請搜索C#拓展方法的定義
最后,只需要在IQueryable對象上調用Search(查詢條件對象)這個函數並傳入參數就可以了
這是我自己定義的查詢條件類
1 public class ProjectInfoDTO 2 { 3 [Search("CompanyName",OperationType.Like)] 4 public string CompanyName { get; set; } 5 6 7 [Search("SYS_CreateTime",OperationType.GreaterThanOrEqual)] 8 public DateTime? CreateTimeStart { get; set; } 9 10 [Search("SYS_CreateTime", OperationType.LessThanOrEqual)] 11 public DateTime? CreateTimeEnd { get; set; } 12 }
類的屬性名稱不一定要與屬性上面Search方法的displayName參數的值相同,但是displayName參數的值必須與查詢列表對象中屬性的名稱相同
這是我查詢的列表
1 var result = (from a in db.Company_BasicInfo.Where(p => p.CompanyID > 0) 2 select new 3 { 4 a.CompanyID, 5 a.CompanyName, 6 a.SYS_CreateTime 7 }).Search(searchOption).OrderByDescending(p => p.SYS_CreateTime).Take(10000).ToList();
OK,現在我不需要管我的查詢條件是什么了,只需要往查詢屬性的對象中傳入對應的值即可
