使用LINQ Expression构建Query Object

  这个问题来源于Apworks应用开发框架的设计。由于命令与查询职责的分离,使得基于CQRS体系结构风格的应用系统的外部存储系统的结构变得简单起来:在“命令”部分,简单地说,只需要 Event Store和Snapshot Store来保存Domain Model;而“查询”部分,则又是基于事件派送与侦听的系统集成。之前我也提到过,“查询”部分由于不牵涉到Domain Model,于是,它的设计应该随意性很大:不需要受到Domain Model的牵制,例如,我们可以根据UI所需的数据源结构进行“查询”库的设计。Greg Young在他的“CQRS Documents”一文中也提到了这样一些相关话题:就“查询”部分而言,虽然也存在“阻抗失衡”效应,但是事件模型与关系模型之间的这种效应,要远远小于对象模型与关系模型之间的“阻抗失衡”效应。这是因为,事件模型本身没有结构,它仅仅表述“该对关系模型做哪些操作”这样的概念。在设计上,Greg Young建议,采用一种非正规的方式设计“查询”数据库,以便尽量减少读取数据时所需的JOIN操作,比如可以选用基于第一范式(1NF)的关系模型。这是一种反范式模型,虽然Greg Young并没有建议根据UI所需的数据源结构进行设计,但思想是相同的:基于事件而不拘泥于对象模型本身。由此引申出来的另一个好处就是外部存储系统架构的随意性:你可以选用任何存储技术和存储媒介,这又给基于系统架构的性能优化提供了便利。因为并非所有的存储架构都支持“表”、“字段”、“存储过程”、“JOIN”这些概念。
  根据上面的简单分析,我们得到一个结论:通常情况下,或许基于CQRS体系结构风格的应用系统更多的是采用的“平整”的外部存储结构,简而言之,就是一个数据访问对象(DAO)对应一张数据表。这也是我所设计的Apworks应用开发框架中默认支持的一种存储结构。有读过Apworks源代码的朋友会发现,在Apworks.Events.Storage命名空间下,有两个定制的DAO:DomainEventDataObject,用于表述领域事件的数据结构,以及SnapshotDataObject,用于表述快照的数据结构,与之相对应的就是数据库中的两张表:DomainEvents和 Snapshots。虽然结构变得这么简单,但是映射关系总还是需要维护的:最简单的就是需要在对象类型名称与数据表名之间,以及对象属性与数据表字段之间建立起映射关系。在Apworks中,这种映射关系是由Apworks.Storage.IStorageMappingResolver接口完成的。有关这个接口的内容不是本文讨论的重点,暂且不深入分析了。
  至此,也许你不会接受我上面的讨论,认为“基于UI设计数据库结构”或者“采用1NF、反范式设计数据库结构”是无法接受的,那么,接下来的讨论可能对你来说意义也不大了。因为下面的问题是以上面的描述为基础的:一个数据访问对象对应一张数据表。不过即使你不认同我的观点,我也建议你继续看完本文。
  Query Object模式

  虽然只是简单的映射,但毕竟不能忽略这样的映射关系。Apworks作为一个应用开发框架,需要提供方便的整合接口,以便今后能够根据不同的客户需求进行扩展。例如在存储部分,数据的增删改查(CRUD)是基于数据访问对象(DAO)的,这样做的一个好处是能够对外部存储系统进行抽象,使得访问存储系统的部分能够无需关系存储系统的细节问题。客户有可能选择SQL Server、Oracle、MySQL等关系型数据库作为存储系统,也可以选择其它的非关系型数据库作为存储系统,因此,我们的设计不能仅仅局限于关系型数据库,我们需要同时考虑其它形式的数据存储产品以便将来能够方便地集成新的存储方案。假设我们要设计一个针对 DomainEventDataObject的“查询”功能,我们需要考虑的问题可能会有(但不一定仅限于):
  * 需要查询对象的哪些属性(或者说与DomainEventDataObject相对应的数据表的哪些字段)
  * 需要根据什么样的条件进行查询
  * 查询是否需要排序
  * 是否只查结果集中的任意一条记录,还是要返回所有的记录
  在Apworks框架的Alpha版本中,查询的方法定义在Apworks.Storage.IStorage接口中。比如,根据给定的查询条件和排序方式,对指定DAO进行查询的方法定义如下:

/// <summary>
/// Gets a list of ordered objects from storage by given selection criteria and order.
/// </summary>
/// <typeparam name="T">The type of the object to get.</typeparam>
/// <param name="criteria">The <c>PropertyBag</c> instance which contains the criteria.</param>
/// <param name="orders">The <c>PropertyBag</c> instance which contains the ordering fields.</param>
/// <param name="sortOrder">The sort order.</param>
/// <returns>A list of ordered objects.</returns>
IEnumerable<T> Select<T>(PropertyBag criteria, PropertyBag orders, SortOrder sortOrder)
where T : class, new();

NET技术使用LINQ Expression构建Query Object,转载需保留来源!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。