Apply SOA Design Patterns with WCF (4) WCF Database Paging & Sorting (数据库端分页和排序)
Original (原创) by Teddy’s Knowledge Base
Content (目录)
(1) WCF Configuration Centralization (WCF配置集中管理)
(2) WCF Automatic Deployment (WCF自动化部署)
(3) WCF Automatic Service Locating (WCF自动化服务定位)
(4) WCF Database Paging & Sorting (WCF数据库分页和排序)
(5) WCF Based ASP.NET DataSouce Control (基于WCF的数据源控件)
(6) 1 + 2 + 3 + 4 + 5 = ?
摘要
本文提供一种支持跨WCF通讯的数据库端分页和排序(以及更多超过你预期的功能)方案。
正文
我们在重新发明轮子吗?
已经有很多数据库端分页和排序的实现方案了,从微软的LINQ to SQL和Entity Framework到无数的ORM框架和基于存储过程的分页和排序实现。
但是,构架得很好的支持跨WCF通信的数据库端分页和排序方案还很少见到。大多数数据库端分页和排序方案要么依赖于不能被序列化的对象,要么暴露了太多SQL相关的信息给客户端,甚至可能允许客户程序直接构造SQL或伪SQL片段,这样就不能很好的避免SQL注入。
所以,我们是在创新而不是重新造轮子。
我们的目标
下面是这个跨WCF通信的分页排序方案的功能列表:
- LINQ风格的强类型查询语言
- 支持分页和排序
- 支持常用的数据库操作和方法
- 避免SQL注入
- 隐藏数据结构和SQL的细节
- 查询对象能改能跨WCF通信
- 多数据库查询语言扩展支持
- 支持和第三方诗句访问组件和ORM类库的整合
类图
各个类的职责
- Criteria – 主要的携带关于一个查询的所有信息的类
- IExpression – 所有构成Criteria的元素的接口
- Expression类– 代表查询中不同类型的表达式
- Parameter Expression类 – 代表不同类型的参数,每个参数包装了一个原生类型的数据值
- IColumn – 所有Column表达式的接口
- Column类 – 代表查询中不同类型的Column
- Condition – 代表一个逻辑表达式,可以参与诸如And,Or,Not这样的逻辑操作
表达式的操作和方法
- 常用的操作和方法应该定义为类的公共方法,并且,如果可能,为每个操作也定义相应的操作符重载
- 对特定数据库的操作和方法应该要么定义在继承的子类中,要么定义为扩展方法
查询示例代码
1 public class TestCriteria : Criteria
2 {
3 public TestCriteria()
4 : base("TestTable", "Test")
5 {
6 }
7
8 public BooleanColumn BooleanColumn = new BooleanColumn("BooleanColumn");
9 public ByteColumn ByteColumn = new ByteColumn("ByteColumn");
10 public Int16Column Int16Column = new Int16Column("Int16Column");
11 public Int32Column Int32Column = new Int32Column("Int32Column");
12 public Int64Column Int64Column = new Int64Column("Int64Column");
13 public DateTimeColumn DateTimeColumn = new DateTimeColumn("DateTimeColumn");
14 public StringColumn StringColumn = new StringColumn("StringColumn", true);
15 public GuidColumn GuidColumn = new GuidColumn("GuidColumn");
16 public DoubleColumn DoubleColumn = new DoubleColumn("DoubleColumn");
17 public DecimalColumn DecimalColumn = new DecimalColumn("DecimalColumn");
18 }
19
20 var criteria = new TestCriteria();
21 criteria.MaxResults(10).AddSortBy(criteria.Int32Column, true).AddSortBy(criteria.StringColumn, false);
22 criteria.And(criteria.Int32Column == 1).Or(criteria.StringColumn.Like("test"));
23
24 criteria.AddResultColumn(DateTimeColumn).AddResultColumn((criteria.Int32Column + 1).As("ID")).AddResultColumn(criteria.StringColumn.As("Name"));
25 criteria.SkipResults(10);
26 criteria.Distinct();
27
28 var queryResult = aWcfService.Query(criteria);
如何使类能够作为WCF服务的参数被序列化
这些类的结构和上面的查询示例代码可能和大多数SQL风格的查询语言很相似,真正的实现难点其实在如何使他们能够作为WCF服务的参数被序列化。下面的要点能帮助实现这个目标:
- 除了Criteria的所有的类应该要么定位为sealed类,要么只有internal的构造函数,这样就能确保在我们定义的程序集之外不会有子类
- 所有的类应该标注为WCF的DataContract,所有的字段应该标注为DataMember
- 所有的类都不应该包含不能被序列化的字段
- 一个类的所有依赖类型都应该被加进这个类的定义的KnownType列表中,例如,因为Criteria类依赖于所有其他的表达式类型,所以,Criteria类的定义应该像下面这样:
1 [DataContract]
2 [KnownType(typeof(BooleanColumn))]
3 [KnownType(typeof(ByteColumn))]
4 [KnownType(typeof(Int16Column))]
5 [KnownType(typeof(Int32Column))]
6 [KnownType(typeof(Int64Column))]
7 [KnownType(typeof(DateTimeColumn))]
8 [KnownType(typeof(StringColumn))]
9 [KnownType(typeof(GuidColumn))]
10 [KnownType(typeof(DoubleColumn))]
11 [KnownType(typeof(DecimalColumn))]
12 [KnownType(typeof(Condition))]
13 [KnownType(typeof(NullExpression))]
14 [KnownType(typeof(BooleanExpression))]
15 [KnownType(typeof(ByteExpression))]
16 [KnownType(typeof(Int16Expression))]
17 [KnownType(typeof(Int32Expression))]
18 [KnownType(typeof(Int64Expression))]
19 [KnownType(typeof(DateTimeExpression))]
20 [KnownType(typeof(StringExpression))]
21 [KnownType(typeof(GuidExpression))]
22 [KnownType(typeof(DoubleExpression))]
23 [KnownType(typeof(DecimalExpression))]
24 [KnownType(typeof(ExpressionCollection))]
25 [KnownType(typeof(BooleanParameterExpression))]
26 [KnownType(typeof(ByteParameterExpression))]
27 [KnownType(typeof(Int16ParameterExpression))]
28 [KnownType(typeof(Int32ParameterExpression))]
29 [KnownType(typeof(Int64ParameterExpression))]
30 [KnownType(typeof(DateTimeParameterExpression))]
31 [KnownType(typeof(StringParameterExpression))]
32 [KnownType(typeof(GuidParameterExpression))]
33 [KnownType(typeof(DoubleParameterExpression))]
34 [KnownType(typeof(DecimalParameterExpression))]
35 public class Criteria
36 {
37 //…
38
39 }
第三方数据访问和ORM类库整合
因为上面讨论的类都不是只能用于特定数据库访问组件的,所以,一个criteria可以被认为是一个查询的元数据,可以被翻译为任意一个实际的数据访问实现。例如,翻译成LINQ to SQL查询语言或者翻译成一个能直接执行的DbCommand。
基于Criteria的WCF服务的好处
- 支持复杂条件的查询
- 更容易的支持分页和排序
- 极大的减少数据库风格的查询类型的服务的数量
提示
- 无论LINQ还是这种强类型的查询语言都有额外的运行时消耗,包括构造criteria和翻译成实际的数据访问语言的内存、CPU运行时间消耗。所以对高性能场景,存储过程还是应该是数据库查询类服务首选的实现方案。
- 在实践中,对企业级应用来说,一般,除了专门用来实现并不容易实现的分页和排序功能,这种基于Criteria的WCF服务更多的还是用来实现原型程序和被用来在最终的应用中作为一个后备服务。我还是会定义包含清晰的业务含义名称的服务,如LoadByID(),GetAllXXX(),并且只要可能就用存储过程来实现。
参考
(1) SOA Design Pattern Catalog: http://www.soapatterns.org///我是结尾符,待续…