经有很多数据库端分页和排序的实现方案了,从微软的LINQ to SQL和Entity Framework到无数的ORM框架和基于存储过程的分页和排序实现。

但是,构架得很好的支持跨WCF通信的数据库端分页和排序方案还很少见到。大多数数据库端分页和排序方案要么依赖于不能被序列化的对象,要么暴露了太多SQL相关的信息给客户端,甚至可能允许客户程序直接构造SQL或伪SQL片段,这样就不能很好的避免SQL注入。

所以,我们是在创新而不是重新造轮子。

我们的目标

下面是这个跨WCF通信的分页排序方案的功能列表:

  • LINQ风格的强类型查询语言
  • 支持分页和排序
  • 支持常用的数据库操作和方法
  • 避免SQL注入
  • 隐藏数据结构和SQL的细节
  • 查询对象能改能跨WCF通信
  • 多数据库查询语言扩展支持
  • 支持和第三方诗句访问组件和ORM类库的整合
类图

NIntegrate.Query

各个类的职责
  • 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服务的好处
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐