使用Expression实现数据的任意字段过滤(2)

上一篇《使用Expression实现数据的任意字段过滤(1)》, 我们实现了通过CriteriaCollectionHandler对象来处理集合数据过滤。通过适当的扩展, 应该可以满足一般的筛选条件应用了。但是在我经历的项目中, 突然有个情况让我措手不及。下面和大家分享下。

这个项目叫WebAD,顾名思义, 就是将AD的管理界面使用Web来实现。 无可避免的要查询AD里面的对象,比如UserPrinciple,即查找某一OU节点下的所有用户。

先感受下UserPrinciple的属性

                       

 再感受下用户的气场:

“我需要用户的邮箱、部门名称、公司名称、密码修改时间…..”

好吧, 这些AD里有, UserPrincipal没有,但通过非公开的ExtensionGet()方法,这些变成可以有。

用户的气场再爆发:“我们还需要按邮箱名称、部门名称、公司名称来过滤!”

OMG,这合理,但,真的要这么做吗?

如果用传统的办法, 只能是按下面的步骤:

1) 从AD按OU节点查得所有的UserPrinciple

2) 将List<UserPrinciple>逐一转换为List<UserModel>, 其中UserModel包括了用户要的那些非公开属性, 通过使用反射取ExtensionGet()方法, 再调用该方法获得指定属性

3) 使用Linq对List<UserModel>进行过滤

大概试了下, 加入AD返回了2000个UserPrincipal对象, 每个对象取10个非公开属性,第2)步大概要用99%以上的时间。 为什么? 大量反射!这可不是EntityFramework,直接在SQLServer端就将过滤处理了,这是WebServer端干的活儿,严重拖累性能。

10分钟过去还得不到查询结果。 您再感受下客户气场……

 

解决的思路, 还是我们之前的ICollectionHandler,在将UserPrinciple 映射成UserModel之前, 先把过滤做了, 然后再把结果转换成UserModel, 这样能大大减少反射使用 的次数。可接下来的问题来了, 之前我们的CriteriaCollectionHandler类型中, 是使用type.GetProperty()方法来获取Public属性, 并进一步获取到属性值的,现在这些间接属性值改怎么办呢?

 

先来感受下代码:

 1  public sealed class AdvanceCriteriaCollectionHandler : CriteriaCollectionHandler
 2     {
 3         private string PropertyKey { get; set; }
 4 
 5 
 6         private MethodInfo GetPropertyMethod { get; set; }
 7 
 8 
 9         public AdvanceCriteriaCollectionHandler(Func<object, string, object> getPropertyMethod, string propertyKey, object target, ComparerEnum comparer)
10             : base("", target, comparer)
11         {
12             GetPropertyMethod = getPropertyMethod.GetMethodInfo();
13             this.PropertyKey = propertyKey;
14         }
15 
16 
17         private IQueryable<T> Filter<T>(IQueryable<T> source)
18         {
19             var type = typeof(T);
20             var parameter = Expression.Parameter(type, "p");   //  
21 
22             var constExpression = Expression.Constant(Target); // 转换为target的类型,以作比较
23             var propertyAccess = Expression.Call(GetPropertyMethod, parameter, Expression.Constant(PropertyKey));
24 
25             Expression comparisionExpression;
26             switch (Comparer)
27             {
28                 case ComparerEnum.Eq:
29                     comparisionExpression = Expression.Equal(propertyAccess, constExpression);
30                     break;
31                 case ComparerEnum.Ne:
32                     comparisionExpression = Expression.NotEqual(propertyAccess, constExpression);
33                     break;
34                 case ComparerEnum.Lt:
35                     comparisionExpression = Expression.LessThan(propertyAccess, constExpression);
36                     break;
37                 case ComparerEnum.Gt:
38                     comparisionExpression = Expression.GreaterThan(propertyAccess, constExpression);
39                     break;
40                 case ComparerEnum.Le:
41                     comparisionExpression = Expression.LessThanOrEqual(propertyAccess, constExpression);
42                     break;
43                 case ComparerEnum.Ge:
44                     comparisionExpression = Expression.GreaterThanOrEqual(propertyAccess, constExpression);
45                     break;
46                 case ComparerEnum.StringLike:
47                     if (!(Target is string))
48                     {
49                         throw new NotSupportedException("StringLike is only suitable for string type property!");
50                     }
51 
52 
53                     var stringContainsMethod = typeof(CriteriaCollectionHandler).GetMethod("StringContains");
54 
55                     comparisionExpression = Expression.Call(stringContainsMethod, propertyAccess, constExpression);
56 
57                     break;
58                 default:
59                     comparisionExpression = Expression.Equal(propertyAccess, constExpression);
60                     break;
61             }
62 
63 
64             var compareExp = Expression.Lambda(comparisionExpression, parameter);
65             var typeArguments = new Type[] { type };
66             var methodName = "Where"; //sortOrder == SortDirection.Ascending ? "OrderBy" : "OrderByDescending";
67             var resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, Expression.Quote(compareExp));
68 
69             return source.Provider.CreateQuery<T>(resultExp);
70         }
71 
72         public override ICollection<T> Execute<T>(ICollection<T> values)
73         {
74             var result = Filter(values.AsQueryable()).ToList();
75             return result;
76         }
77     }

 

区别在哪呢? 构造函数中多了一个委托对象,这个委托对象就是用来处理取属性值的方法。三个参数分别对应了元素, 元素属性名称, 元素属性值(返回)。

其他的没有变化

 

使用示例(伪码)

1 var criteria1 = new AdvanceCriteriaCollectionHandler(PrincipalExtensions.GetExtension, "Department", "HR" , comparerEnum.Eq);   // department = HR
2 var   result = criteria1.Execute()

其中PrincipalExtensions.GetExtension 如下

 1 public static class PrincipalExtensions
 2  {  3 private static readonly MethodInfo ExtensionSet = typeof(Principal).GetMethod("ExtensionSet", BindingFlags.NonPublic | BindingFlags.Instance);  4 private static readonly ADSIUserHandler userHandler = new ADSIUserHandler();  5 public static void SetExtension<T>(this Principal principal, String key, T value)  6  {  7 ExtensionSet.Invoke(principal, new object[] { key, value });  8  9  } 10 private static readonly MethodInfo ExtensionGet = typeof(Principal).GetMethod("ExtensionGet", BindingFlags.NonPublic | BindingFlags.Instance); 11 12 13 public static T GetExtension<T>(this Principal principal, String key) 14  { 15 try 16  { 17 var values = (object[])ExtensionGet.Invoke(principal, new[] { key }); 18 19 if (values == null || values.Length == 0) 20  { 21 return default(T); 22  } 23 return (T)values[0]; 24 25  } 26 catch 27  { 28 return default(T); 29  } 30  } 31 32 33 public static object GetExtension(object principal, String key) 34  { 35 36 try 37  { 38 object[] values = (object[])ExtensionGet.Invoke(principal, new[] { key }); 39 if (values == null || values.Length == 0) 40  { 41 return null; 42  } 43 return values[0]; 44 45  } 46 catch 47  { 48 return null; 49  } 50  } 51 52 53 }

 

然后在获取所有UserPrinciple的后面立刻加上集合过滤(伪码):

1  ICollection<UserPrincipal> allUsers = ADSIHelper.GetUsers(recursive, searchBase, keywords).OfType<UserPrincipal>().ToList();
2 
3 
4             if (advCriteria!= null)  //advanced criteria collection handler
5             {
6                 allUsers = advCriteria.Execute(allUsers);
7             }
 return allUsers.ToModels();

 

重新估算下, 之前是2000*10 = 20,000次反射调用

现在,假如删选完成后只有100个结果, 则反射调用的次数是 2000*1 + 100*10 = 3,000 次反射调用。

不仅仅速度会明显变快,而且能和之前的CollectionHandler处理的思路保持一致,同样能支持任意字段的过滤。

 

 

使用Expression来实现对集合的任意字段过滤先介绍到这里。 除了筛选过滤,常见的还有排序和分页, 后面我再陆续介绍。

转载于:https://www.cnblogs.com/huwz/p/ExpressionFilter2.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值