C# Expression建立ICollectionView的動態多重過濾條件
阿新 • • 發佈:2021-08-06
ICollectionView有一個Filter屬性,接受一個Predicate<object>的委託 來生成過濾檢視
準備一個實體資料類
1 /// <summary> 2 /// 測試資料類 3 /// </summary> 4 public class UserDataProxy : DynamicObject 5 { 6 private Dictionary<string, object> _dict = new Dictionary<string, object>(); 7 8public string Color { get; set; } 9 10 public Guid Guid { get; set; } 11 12 public string Layer { get; set; } 13 14 public string Length { get; set; } 15 16 public string Name { get; set; } 17 18 public string Type { get; set; } 19 20 /// <summary>21 /// 新增動態屬性 22 /// </summary> 23 /// <param name="propertyName"></param> 24 /// <param name="value"></param> 25 public void AddDynamicProperty(string propertyName, object value) 26 { 27 _dict[propertyName] = value; 28 }29 30 public IEnumerable<string> GetAllProPertyNames() 31 { 32 var list = new List<string>() 33 { 34 "Guid","Type","Layer","Name","Color" 35 }; 36 list.AddRange(GetDynamicMemberNames()); 37 return list; 38 } 39 40 /// <summary> 41 /// 重寫獲取動態屬性的方法 42 /// </summary> 43 /// <returns></returns> 44 public override IEnumerable<string> GetDynamicMemberNames() 45 { 46 return _dict.Keys; 47 } 48 49 /// <summary> 50 /// 獲取動態屬性的方法 51 /// </summary> 52 /// <param name="propertyName"></param> 53 /// <returns></returns> 54 public object GetDynamicValue(string propertyName) 55 { 56 if (_dict.Keys.Contains(propertyName)) 57 { 58 return _dict[propertyName]; 59 } 60 return null; 61 } 62 63 public void RemoveDynamicProperty(string propertyName) 64 { 65 if (_dict.Keys.Contains(propertyName)) 66 { 67 _dict.Remove(propertyName); 68 } 69 } 70 71 /// <summary> 72 /// 重寫獲取動態屬性 73 /// </summary> 74 /// <param name="binder"></param> 75 /// <param name="result"></param> 76 /// <returns></returns> 77 public override bool TryGetMember(GetMemberBinder binder, out object result) 78 { 79 var key = binder.Name; 80 return _dict.TryGetValue(key, out result); 81 } 82 83 /// <summary> 84 /// 重寫設定動態屬性 85 /// </summary> 86 /// <param name="binder"></param> 87 /// <param name="value"></param> 88 /// <returns></returns> 89 public override bool TrySetMember(SetMemberBinder binder, object value) 90 { 91 var key = binder.Name; 92 _dict[key] = value ?? default; 93 return true; 94 } 95 }
準備一個運算子列舉
1 /// <summary> 2 /// 過濾運算子列舉 3 /// </summary> 4 [Flags] 5 public enum EFilterOperator 6 { 7 [Description("=")] 8 Equal = 1, 9 10 [Description("!=")] 11 NotEqual = 2, 12 13 [Description(">")] 14 GreaterThan = 4, 15 16 [Description("<")] 17 LessThan = 8, 18 19 [Description(">=")] 20 GreaterThanOrEqual = 16, 21 22 [Description("<=")] 23 LessThanOrEqual = 32, 24 25 [Description("s")] 26 StartsWith = 64, 27 28 [Description("e")] 29 EndWith = 128, 30 31 [Description("<>")] 32 Contains = 256, 33 34 [Description("><")] 35 NotContains = 512, 36 }
準備一個表示式樹輔助類
1 /// <summary> 2 /// 表示式輔助類 3 /// </summary> 4 public class ExpressionTreeHelper 5 { 6 /// <summary> 7 /// 轉換屬性表示式 o as UserDataProxy 8 /// </summary> 9 /// <typeparam name="T"></typeparam> 10 /// <param name="type"></param> 11 /// <returns></returns> 12 public static UnaryExpression ConvertTo<T>(ParameterExpression parameterExpression) 13 { 14 return Expression.TypeAs(parameterExpression, typeof(T)); 15 } 16 17 /// <summary> 18 /// 建立條件表示式 i.Name.Contains("xx") 19 /// </summary> 20 /// <param name="memberExpression">屬性</param> 21 /// <param name="condition">條件</param> 22 /// <param name="content"></param> 23 /// <returns></returns> 24 public static Expression CreateCondition(MemberExpression memberExpression, EFilterOperator condition, string content) 25 { 26 if (content == null) 27 { 28 return null; 29 } 30 var flag = double.TryParse(content, out double num);//轉換數字 能夠轉換是數字,不能就是文字 31 32 BinaryExpression exp;//二位運算子表示式 33 34 Expression left;//左側表示式 35 ConstantExpression right;//右側表示式 36 37 if (flag) 38 { 39 //把屬性轉換為數字 因為有的屬性可能是字串 40 left = Expression.Call(null, typeof(double).GetMethod("Parse", new Type[] { typeof(string) }), new Expression[] { memberExpression }); 41 42 //右側的表示式 43 right = Expression.Constant(num, typeof(double)); 44 } 45 else 46 { 47 //把屬性轉換為字串 48 left = Expression.Call(memberExpression, typeof(string).GetMethod("ToString", new Type[] { }), new Expression[] { }); 49 50 //右側的表示式 51 right = Expression.Constant(content, typeof(string)); 52 } 53 54 MethodCallExpression callExp; 55 switch (condition) 56 { 57 case EFilterOperator.Equal: 58 if (!flag) 59 { 60 return null; 61 } 62 exp = Expression.Equal(left, right); 63 return exp; 64 65 case EFilterOperator.NotEqual: 66 if (!flag) 67 { 68 return null; 69 } 70 exp = Expression.NotEqual(left, right); 71 72 return exp; 73 74 case EFilterOperator.GreaterThan: 75 if (!flag) 76 { 77 return null; 78 } 79 exp = Expression.GreaterThan(left, right); 80 81 return exp; 82 83 case EFilterOperator.LessThan: 84 if (!flag) 85 { 86 return null; 87 } 88 exp = Expression.LessThan(left, right); 89 90 return exp; 91 92 case EFilterOperator.GreaterThanOrEqual: 93 if (!flag) 94 { 95 return null; 96 } 97 exp = Expression.GreaterThanOrEqual(left, right); 98 99 return exp; 100 101 case EFilterOperator.LessThanOrEqual: 102 if (!flag) 103 { 104 return null; 105 } 106 exp = Expression.LessThanOrEqual(left, right); 107 108 return exp; 109 110 case EFilterOperator.StartsWith: 111 callExp = Expression.Call(left, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), new Expression[] { right }); 112 113 return callExp; 114 115 case EFilterOperator.EndWith: 116 callExp = Expression.Call(left, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }), new Expression[] { right }); 117 return callExp; 118 119 case EFilterOperator.Contains: 120 121 callExp = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), new Expression[] { right }); 122 123 return callExp; 124 125 case EFilterOperator.NotContains: 126 callExp = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), new Expression[] { right }); 127 var not = Expression.Not(callExp);//取反 128 129 return not; 130 }; 131 132 return null; 133 } 134 135 /// <summary> 136 /// 獲取屬性表示式 137 /// </summary> 138 /// <param name="expression"></param> 139 /// <param name="propertyName"></param> 140 public static MemberExpression GetPropertyExp(Expression expression, string propertyName) 141 { 142 string orginalName; 143 144 switch (propertyName) 145 { 146 case "標識": 147 orginalName = "Guid"; 148 149 break; 150 151 case "顏色": 152 orginalName = "Color"; 153 break; 154 155 case "型別": 156 orginalName = "Type"; 157 break; 158 159 case "圖層": 160 orginalName = "Layer"; 161 break; 162 163 case "名稱": 164 orginalName = "Name"; 165 break; 166 167 default: 168 orginalName = propertyName; 169 break; 170 } 171 172 return Expression.Property(expression, orginalName); 173 } 174 175 /// <summary> 176 /// 合併條件表示式 177 /// </summary> 178 /// <param name="left"></param> 179 /// <param name="right"></param> 180 /// <returns></returns> 181 public static BinaryExpression JionCondition(Expression left, Expression right) 182 { 183 return Expression.AndAlso(left, right); 184 } 185 186 /// <summary> 187 /// 此處可省略 188 /// </summary> 189 /// <param name="propertyName"></param> 190 /// <param name="condition"></param> 191 /// <param name="content"></param> 192 /// <returns></returns> 193 [Obsolete("", true)] 194 private Func<UserDataProxy, bool> CreateLambda(string propertyName, EFilterOperator condition, string content) 195 { 196 string orginalName; 197 198 switch (propertyName) 199 { 200 case "標識": 201 orginalName = "Guid"; 202 203 break; 204 205 case "顏色": 206 orginalName = "Color"; 207 break; 208 209 case "型別": 210 orginalName = "Type"; 211 break; 212 213 case "圖層": 214 orginalName = "Layer"; 215 break; 216 217 case "名稱": 218 orginalName = "Name"; 219 break; 220 221 default: 222 orginalName = propertyName; 223 break; 224 } 225 226 var paraExp = Expression.Parameter(typeof(UserDataProxy), "u");//原始型別 227 228 var propertyExp = Expression.Property(paraExp, orginalName);//屬性定義 229 230 if (content == null) 231 { 232 return null; 233 } 234 var flag = double.TryParse(content, out double num);//轉換數字 能夠轉換是數字,不能就是文字 235 236 Expression<Func<UserDataProxy, bool>> lambda = null;//最終結果 237 BinaryExpression exp;//二位運算子表示式 238 239 Expression left;//左側表示式 240 ConstantExpression right;//右側表示式 241 242 if (flag) 243 { 244 left = Expression.Call(null, typeof(double).GetMethod("Parse", new Type[] { typeof(string) }), new Expression[] { propertyExp }); 245 246 right = Expression.Constant(num, typeof(double)); 247 } 248 else 249 { 250 left = Expression.Call(propertyExp, typeof(string).GetMethod("ToString", new Type[] { }), new Expression[] { });//屬性轉換為字串 251 right = Expression.Constant(content, typeof(string)); 252 } 253 254 MethodCallExpression callExp; 255 switch (condition) 256 { 257 case EFilterOperator.Equal: 258 if (!flag) 259 { 260 return null; 261 } 262 exp = Expression.Equal(left, right); 263 lambda = Expression.Lambda<Func<UserDataProxy, bool>>(exp, new ParameterExpression[] { paraExp }); 264 break; 265 266 case EFilterOperator.NotEqual: 267 if (!flag) 268 { 269 return null; 270 } 271 exp = Expression.NotEqual(left, right); 272 lambda = Expression.Lambda<Func<UserDataProxy, bool>>(exp, new ParameterExpression[] { paraExp }); 273 break; 274 275 case EFilterOperator.GreaterThan: 276 if (!flag) 277 { 278 return null; 279 } 280 exp = Expression.GreaterThan(left, right); 281 lambda = Expression.Lambda<Func<UserDataProxy, bool>>(exp, new ParameterExpression[] { paraExp }); 282 break; 283 284 case EFilterOperator.LessThan: 285 if (!flag) 286 { 287 return null; 288 } 289 exp = Expression.LessThan(left, right); 290 lambda = Expression.Lambda<Func<UserDataProxy, bool>>(exp, new ParameterExpression[] { paraExp }); 291 break; 292 293 case EFilterOperator.GreaterThanOrEqual: 294 if (!flag) 295 { 296 return null; 297 } 298 exp = Expression.GreaterThanOrEqual(left, right); 299 lambda = Expression.Lambda<Func<UserDataProxy, bool>>(exp, new ParameterExpression[] { paraExp }); 300 break; 301 302 case EFilterOperator.LessThanOrEqual: 303 if (!flag) 304 { 305 return null; 306 } 307 exp = Expression.LessThanOrEqual(left, right); 308 lambda = Expression.Lambda<Func<UserDataProxy, bool>>(exp, new ParameterExpression[] { paraExp }); 309 break; 310 311 case EFilterOperator.StartsWith: 312 callExp = Expression.Call(left, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), new Expression[] { right }); 313 lambda = Expression.Lambda<Func<UserDataProxy, bool>>(callExp, new ParameterExpression[] { paraExp }); 314 break; 315 316 case EFilterOperator.EndWith: 317 callExp = Expression.Call(left, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }), new Expression[] { right }); 318 lambda = Expression.Lambda<Func<UserDataProxy, bool>>(callExp, new ParameterExpression[] { paraExp }); 319 break; 320 321 case EFilterOperator.Contains: 322 323 callExp = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), new Expression[] { right }); 324 lambda = Expression.Lambda<Func<UserDataProxy, bool>>(callExp, new ParameterExpression[] { paraExp }); 325 break; 326 327 case EFilterOperator.NotContains: 328 callExp = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), new Expression[] { right }); 329 lambda = Expression.Lambda<Func<UserDataProxy, bool>>(Expression.Not(callExp), new ParameterExpression[] { paraExp }); 330 break; 331 }; 332 333 return lambda.Compile(); 334 } 335 }
執行測試
1 internal class Program 2 { 3 public static Func<object, bool> Show() 4 { 5 return null; 6 } 7 8 private static void Main(string[] args) 9 { 10 var list = new List<object>() 11 { 12 new UserDataProxy(){Color="紅色",Name="王義夫",Length="120"}, 13 new UserDataProxy(){Color="綠色",Name="王受人",Length="2120"}, 14 new UserDataProxy(){Color="藍色",Name="王馬子",Length="920"}, 15 new UserDataProxy(){Color="白色",Name="張三義",Length="1120"}, 16 new UserDataProxy(){Color="紫色",Name="裡義夫",Length="1120"}, 17 new UserDataProxy(){Color="紅色",Name="王義夫",Length="1020"}, 18 new UserDataProxy(){Color="紅色",Name="王首任",Length="750"}, 19 new UserDataProxy(){Color="紅色",Name="李義夫",Length="1210"}, 20 new UserDataProxy(){Color="紅色",Name="勇義夫",Length="1110"}, 21 }; 22 23 Console.WriteLine($"過濾之前\n"); 24 list.ForEach(user => 25 { 26 var uu = user as UserDataProxy; 27 28 Console.WriteLine($"Color:{uu.Color},Length:{uu.Length},Name:{uu.Name}"); 29 }); 30 31 var collection = CollectionViewSource.GetDefaultView(list); 32 33 //第一個條件 34 var p1 = "Color"; 35 var c1 = EFilterOperator.Contains; 36 var t1 = "紅"; 37 38 //第二個條件 39 var p2 = "Length"; 40 var c2 = EFilterOperator.GreaterThan; 41 var t2 = "1000"; 42 43 //第三個條件 44 var p3 = "Name"; 45 var c3 = EFilterOperator.NotContains; 46 var t3 = "王"; 47 48 //生成條件1 49 var o = Expression.Parameter(typeof(object)); 50 51 var objExp = ExpressionTreeHelper.ConvertTo<UserDataProxy>(o); 52 53 var m1 = ExpressionTreeHelper.GetPropertyExp(objExp, p1); 54 55 var condition1 = ExpressionTreeHelper.CreateCondition(m1, c1, t1); 56 57 //生成條件2 58 var m2 = ExpressionTreeHelper.GetPropertyExp(objExp, p2); 59 60 var condition2 = ExpressionTreeHelper.CreateCondition(m2, c2, t2); 61 62 //生成條件3 63 var m3 = ExpressionTreeHelper.GetPropertyExp(objExp, p3); 64 65 var condition3 = ExpressionTreeHelper.CreateCondition(m3, c3, t3); 66 67 //合併條件 68 var r1 = ExpressionTreeHelper.JionCondition(condition1, condition2);//第一次過濾 69 70 //r1 = ExpressionTreeHelper.JionCondition(r1, condition3);//新增條件後過濾 71 72 var s = Expression.Lambda<Predicate<object>>(r1, o);//建立Lambda表示式 73 74 var func = s.Compile();//編譯成委託 75 76 if (func != null) 77 { 78 //屬性可能為null,在此處丟擲異常 79 try 80 { 81 collection.Filter = func; 82 collection.Refresh(); 83 } 84 catch (Exception ex) 85 { 86 Console.WriteLine($"{ex.Message}"); 87 } 88 } 89 90 Console.WriteLine($"過濾之後\n"); 91 foreach (var item in collection) 92 { 93 var u = item as UserDataProxy; 94 95 Console.WriteLine($"Color:{u.Color},Length:{u.Length},Name:{u.Name}"); 96 } 97 } 98 }
第一次過濾結果
第二次過濾結果