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

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

其他的没有变化

使用示例(伪码)

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

其中PrincipalExtensions.GetExtension 如下

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

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

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

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

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

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

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

使用Expression实现数据的任意字段过滤(2)的更多相关文章

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

    在项目常常要和数据表格打交道. 现在BS的通常做法都是前端用一个js的Grid控件, 然后通过ajax的方式从后台加载数据, 然后将数据和Grid绑定. 数据往往不是一页可以显示完的, 所以要加分页: ...

  2. 齐博x1 万能fun 调用任意数据表 任意字段就是这么任性调用

    列举了几个常用的查询进行简单封装,虽然系统也有内置的但是很多人不大会就二次封装简化了一下. 这里只封装了一个条件 多个条件的自己再封装或者用标签解决比较好 这里只是说fun可以万能调用 1获取任意表的 ...

  3. 写出java8实现对List<User>中的username字段过滤出不等于张三的数据

    写出java8实现对List<User>中的username字段过滤出不等于张三的数据... 对...这个是一道面试题.当时没有看过java8的新特性...所以有点懵. 看完之后感觉 真. ...

  4. Python黑客编程基础3网络数据监听和过滤

    网络数据监听和过滤 课程的实验环境如下: •      操作系统:kali Linux 2.0 •      编程工具:Wing IDE •      Python版本:2.7.9 •      涉及 ...

  5. 处理json数据的空数据为任意字符

    处理json数据的空数据为任意字符 有时候从后台返回来的数据需要处理一下,根据实际开发需求,不能在页面上直接显示空字符,需要显示为"无内容"或者其他字段,而有些json数据结构比较 ...

  6. ASP.NET实现二维码 ASP.Net上传文件 SQL基础语法 C# 动态创建数据库三(MySQL) Net Core 实现谷歌翻译ApI 免费版 C#发布和调试WebService ajax调用WebService实现数据库操作 C# 实体类转json数据过滤掉字段为null的字段

    ASP.NET实现二维码 using System;using System.Collections.Generic;using System.Drawing;using System.Linq;us ...

  7. MVC中构建Linq条件、排序、Selector字段过滤

    代码: System.Linq.Expressions.Expression<Func<Domain.S_ROLE, bool>> expressWhere1 = (c =&g ...

  8. 如何在K3 WISE BOS集成开发工具中自定义字段过滤条件

    1.结论 对于输入过滤条件后BOS报“列名不正确”的过滤条件,要在列名前增加x2标识 无效的过滤 FNumber ,,,,,) 正确的过滤 x2.FNumber ,,,,,) 2.完全可以不看的探索过 ...

  9. SQL批量更新数据库中所有用户数据表中字段类型为tinyint为int

    --SQL批量更新数据库中所有用户数据表中字段类型为tinyint为int --关键说明:--1.从系统表syscolumns中的查询所有xtype='48'的记录得到类型为[tinyint]的字段- ...

随机推荐

  1. 用FSM一键制作逐帧动画雪碧图 Vue2 + webpack

    因为工作需要要将五六十张逐帧图拼成雪碧图,网上想找到一件制作工具半天没有找到,就自己用canvas写了一个. 写成之后就再没有什么机会使用了,因此希望有人使用的时候如果遇到bug了能及时反馈给我. 最 ...

  2. var和dynamic的区别

    1.var 1.均是声明动态类型的变量. 2.在编译阶段已经确定类型,在初始化的时候必须提供初始化的值. 3.无法作为方法参数类型,也无法作为返回值类型. 2.dynamic 1.均是声明动态类型的变 ...

  3. Twproject Gantt开源甘特图功能扩展

    1.Twproject Gantt甘特图介绍 Twproject Gantt 是一款基于 jQuery 开发的甘特图组件,也可以创建其它图表,例如任务树(Task Trees).内置编辑.缩放和 CS ...

  4. 【WPF】日常笔记

    本文专用于记录WPF开发中的小细节,作为备忘录使用. 1. 关于绑定: Text ="{Binding AnchorageValue,Mode=TwoWay,UpdateSourceTrig ...

  5. 使用Git Bash远程添加分支和简单部署你的静态页面

    新建一个分支:git branch mybranch(mybranch你的分支名字) 切换到你的新分支: git checkout mybranch 将新分支发布在github上: git push ...

  6. cesium核心类Viewer简介

    1.简单描述Viewer Viewer类是cesium的核心类,是地图可视化展示的主窗口,cesium程序应用的切入口,扮演必不可少的核心角色. 官网的英文解析如下: A base widget fo ...

  7. ASP.NET MVC 系列随笔汇总[未完待续……]

    ASP.NET MVC 系列随笔汇总[未完待续……] 为了方便大家浏览所以整理一下,有的系列篇幅中不是很全面以后会慢慢的补全的. 学前篇之: ASP.NET MVC学前篇之扩展方法.链式编程 ASP. ...

  8. Spring集成MyBatis

    本文原创,原文地址为http://www.cnblogs.com/fengzheng/p/5045105.html 如果觉得Hibernate不够灵活,可以尝试用Mybatis.相比于Hibernat ...

  9. 使用R画地图数据

    用R画地图数据 首先,从这里下载中国地图的GIS数据,这是一个压缩包,完全解压后包含三个文件(bou2_4p.dbf.bou2_4p.shp和bou2_4p.shx),将这三个文件解压到同一个目录下. ...

  10. Protobuf使用规范分享

    一.Protobuf 的优点 Protobuf 有如 XML,不过它更小.更快.也更简单.它以高效的二进制方式存储,比 XML 小 3 到 10 倍,快 20 到 100 倍.你可以定义自己的数据结构 ...