手机版

怎么用查询表达式表情建立查询对象模式

时间:2021-08-05 来源:互联网 编辑:宝哥软件园 浏览:

类型:数据库类大小:1.7M语言:英语评分:5.0标签:立即下载。这个问题来自于Apworks应用开发框架的设计。由于命令和查询职责的分离,基于CQRS架构风格的应用系统外部存储系统结构变得简单:在“命令”部分,简单来说,只需要事件存储和快照存储来保存域模型;“查询”部分是基于事件调度和拦截的系统集成。正如我之前提到的,因为“查询”部分不涉及领域模型,所以它的设计应该是任意的:它不需要受到领域模型的约束。比如我们可以根据UI需要的数据源结构来设计“查询”库。Greg Young在他的文章《CQRS文献》中也提到了一些相关的话题:就“查询”而言,虽然存在“阻抗不平衡”效应,但事件模型和关系模型之间的效应远远小于对象模型和关系模型之间的效应。这是因为事件模型本身没有结构,只是表达了“如何处理关系模型”的概念。在设计上,Greg Young建议对数据库的“查询”应该采用非正式的方式进行设计,这样在读取数据时可以尽量减少JOIN操作,例如可以使用基于第一范式(1NF)的关系模型。这是一个反范式模型。虽然Greg Young不建议按照UI要求的数据源结构进行设计,但思路是一样的:基于事件,而不是拘泥于对象模型本身。由此衍生出的另一个优势是外部存储系统架构的随机性:可以选择任意存储技术和存储介质,为基于系统架构的性能优化提供了便利。因为不是所有的存储体系结构都支持“表”、“字段”、“存储过程”和“连接”的概念。根据上面的简单分析,我们得到一个结论:一般来说,也许基于CQRS架构风格的应用系统更多的是一个“扁平”的外部存储结构,简而言之,一个数据访问对象(DAO)对应一个数据表。这也是我设计的Apworks应用开发框架中默认支持的存储结构。看过Apworks源代码的朋友会发现,在Apworks这个命名空间下,有两个定制的Dao。事件.存储:DomainEventDataObject,用于表示DomainEvents的数据结构,SnapshotDataObject,用于表示快照的数据结构,对应数据库中的两个表:域事件和快照。虽然结构变得如此简单,但是映射关系还是需要维护的:最简单的就是建立对象类型名称和数据表名称之间,对象属性和数据表字段之间的映射关系。在apworks中,这种映射关系由Apworks完成。storage.istorageappingresolver接口。这个界面的内容不是本文的重点,暂时不做深入分析。在这一点上,也许你不会接受我上面的讨论,认为“基于UI设计数据库结构”或者“用1NF和反范式设计数据库结构”是不可接受的。那么,下面的讨论可能对你来说意义不大。因为下面的问题是基于上面的描述:一个数据访问对象对应一个数据表。但是即使你不同意我的观点,我建议你继续阅读这篇文章。查询对象模式只是一个简单的映射,但这种映射关系毕竟不能忽略。作为一个应用开发框架,Apworks需要提供一个方便的集成接口,以便将来可以根据不同的客户需求进行扩展。例如,在存储部分,CRUD基于数据访问对象(DAO)。这样做的一个好处是可以抽象外部存储系统,这样对存储系统的访问就可以避开关系存储系统的细节。

客户可以选择SQL Server、Oracle、MySQL等关系型数据库作为存储系统,也可以选择其他非关系型数据库作为存储系统。因此,我们的设计不能局限于关系数据库,我们需要同时考虑其他形式的数据存储产品,以便将来可以方便地集成新的存储方案。假设我们要为DomainEventDataObject设计一个“查询”函数,我们需要考虑的问题可能包括(但不一定局限于):需要根据什么条件查询对象的哪些属性(或者与DomainEventDataObject对应的数据表的哪些字段),查询是否需要排序,是否只检查结果集中的任意一条记录,或者是否返回Apworks框架Alpha版本中的所有记录,查询方法在Apworks中定义。存储。历史界面。例如,根据给定的查询条件和排序方法,对指定DAO的查询方法定义如下:1:////summary 2:///按照给定的选择标准和顺序从存储中获取有序对象的列表。3:////summary 43360///type param name=' T '要获取的对象的类型。/type param 5:///param name=' criteria '包含该条件的CpPropertyBag/c实例。/param 6:///param name=' orders '包含排序字段的CpPropertyBag/c实例。/param 7:///param name=' sort order '排序顺序。/param 8:///returns有序对象列表。/returns 9: IEnumerablet select T(PropertyBag条件,property bag订单,SortOrder sortOrder)10:其中T :类,new();这个方法是泛型方法,泛型类型是DAO的类型。它接受三个参数:前两个是用于指定查询条件和排序字段的PropertyBag,最后一个是用于指定排序方法的SortOrder。之所以采用PropertyBag而不是接受SQL字符串,不仅仅是因为框架本身未来需要方便地支持非关系数据库,更重要的是,虽然SQL已经成为行业标准,但实际上不同的关系数据库产品对SQL的支持和实现方式是不同的,有些关系数据库产品可能只支持SQL的某些子集。单纯使用SQL字符串作为Select方法的参数显然是不合理的。实际上,Apworks。存储。历史实现了查询对象模式。Martin Fowler在PoEAA(《企业应用架构模式》)中有以下几点供读者参考:“编程语言可以支持SQL语句,但大多数开发人员并不熟悉。此外,您需要知道数据库设计方案,以便构建查询。您可以通过创建一个特殊的查询方法来避免这种情况,该方法隐藏了SQL的内部参数化方法。然而,很难构造更特殊的查询。而且,如果数据库设计方案发生变化,就需要复制成SQL语句。”“查询对象的一个共同特征是,它们可以通过使用内存对象的语言而不是数据库方案来描述查询。这意味着我可以使用对象和域名来代替表名和列名。对象和数据库是否具有相同的结构并不重要,但是如果它们之间存在差异,查询对象是有用的。为了实现这样的视角变化,查询对象需要知道数据库结构是如何映射到对象结构的,而这个函数实际上使用了元数据映射”[daxnet注意:如上所述,在Apworks框架中,这种元数据映射的实现是istorageappingresolver]“为了描述任何查询,都需要一个灵活的查询对象。然而,应用程序通常可以用远远少于SQL的所有功能来完成这项任务。在这种情况下,您的查询对象会更简单。它不能代表任何东西,但它可以满足特定的需求。此外,当需要更多的函数时,通常不比从头创建一个全方位的查询对象更麻烦。因此,我们应该为当前需求创建一个功能最小化的查询对象,并随着需求的增加而改进这个查询对象。”以上三点让我觉得很感动,尤其是第三点。

目前基于PropertyBag的设计只能支持AND连接的查询条件,比如像“WHERE a=va AND b=VB AND c=VC……”这样的查询,虽然这个查询在AP works的alpha版本中已经足够了,但是不具备可扩展性。基于关系数据库Apworks的内存设计。RdbmsStorage已经把这个逻辑写死了。如果我们需要复杂的查询,这种方法不仅无能,而且无法扩展。PropertyBag应该退出。在下一个版本的Apworks中,我使用了LINQ表达式。NET代替PropertyBag,并引入一个WhereClauseBuilder的对象,它根据LINQ表达式为关系数据库生成WHERE子句。使用LINQ表达式的优点如下:LINQ表达式是下的查询标准。NET,大多数存储系统产品都可以为LINQ表达式提供查询解决方案。即使他们不提供,他们也可以定制自己的提供商,尽管这有点麻烦。然而,可以认识到,LINQ表达式可以通过使用内存对象的语言而不是数据库方案来完美地描述查询。语言的集成给开发者带来了更多的便利。在Apworks中,规范是基于LINQ表达式的,因此,基于规范的查询可以通过Apworks来实现。存储、历史和接口的统一是一个技术问题:如何将LINQ表达式转换成WHERE子句,以便Apworks的查询对象。存储。历史可以使用这个WHERE子句来构造SQL语句,然后通过ADO.NET直接访问数据库?Apworks采用表达式访问者的方案,使用表达式访问者遍历表达式树,然后生成WHERE子句。在讨论表达式访问者之前,让我们回顾一下对象结构和访问者模式。Visitor模式网上关于Visitor模式的文章太多了,有相当一部分讨论的比较透彻,就不多说了。总之,Visitor模式在处理复杂的对象结构时非常自然:它可以遍历结构中的每个对象,然后以不同的方式处理不同的对象类型。看起来好像已经为这些对象扩展了一些方法。之前我用Visitor模式来验证程序配置节点的合理性。当节点类型增加时,仅通过扩展Visitor就可以非常方便地实现新的验证逻辑。模式属于模式,不同的应用场景和实现方式是不同的。经典的Visitor示例通常使用函数重载(多态)并结合Composite模式来说明问题,但实际上Visitor并不一定需要使用函数重载,也不能只在Composite上使用。Expression Visitor的实现不同于Visitor的这个经典案例。表达式访问者在系统下有一个抽象的表达式访问者类。表达式命名空间。我们只需要继承这个抽象类,并在其中重写一些Visit方法,就可以实现WHERE子句的生成。我不打算在这里继续研究ExpressionVisitor如何遍历表达式树,但我将描述一些关于如何生成WHERE子句的细节。支持哪些操作?LINQ表达式有85种类型,但并非所有类型都在SQL中受支持。目前,ApwORks打算支持常用的条件运算,如大于、大于或等于、小于、小于或等于、不等于、and常用的逻辑运算:哪些方法(函数)支持and,或者不支持AND?目前Apworks支持的方法只有三种:对象。等于,字符串。开始于和字符串。以.对象。Equals将被翻译成“object=value”,和string。开始于和字符串。EndsWith将被翻译成“LIKE”子句来支持内联函数和变量?目前,只支持变量,不支持内联函数。

例如,可以通过以下方式指定表达式:1: int a=get age();2: ExpressionFuncEmployee,bool expr=p=p . age . equals(a);不能按以下方式指定表达式:1: expression conferenceemployee,bool Expr=p=p . age . equals(get age());支持扩张?当然,您只需要继承现有的ExpressionVisitor类并重写它的一些方法,就可以在Apworks下为当前Apworks版本中的关系数据库定义IWhereClauseBuilder接口。存储。构建器命名空间。以及一个抽象实现:AP works . storage . builders . where子句builder类,它不仅实现了IWhereClauseBuilder接口,还继承了抽象类system . linq . expressions . expression visitor,因此,where子句生成的主题逻辑就在这个类中。SqlWhereClauseBuilder类继承了WhereClauseBuilder类,以便实现特定于SQL Server语法的WHERE子句生成器。作为apworks的源代码。storage . builders . where子句构建器类比较长,这里就不贴了。读者朋友们请【点击此处】查看本课程所有源代码。与规范的集成在《EntityFramework之领域驱动设计实践(十):规约模式》中,我提出了基于。NET。为了迎合的支持。NET为LINQ表达式,规范模式的实现也采用LINQ表达式。但是将原来的IsSatisfiedfiedBy方法改为直接使用LINQ表达式得到结果:1:公共接口是specification t 23360 { 3: bool issatifiedby(t obj);4:表达式uncT,bool表达式{ get}5: }6: 7:公共抽象类specification T : is specification T 8: { 9: 10: # region is specification members 11: 123:公共虚拟bool issatifiedby(T obj)13: { 14: }返回此。表达式. Compile()(obj);15: }16: 17:公共抽象表达式FuncT,bool Expression { get} 18: 1933 360 # end region 2033 360 }回看Select方法,我们发现第一个参数使用了ExpressionFuncT,bool代替了PropertyBag,但是现在我们可以直接使用is Specification接口,所以我们的Query Object可以使用specification模式支持数据查询。执行过程和客户端调用示例基于以上讨论,Select方法的定义已经从使用PropertyBag作为查询条件变为使用ISpecification接口。

注意:订单参数仍然使用PropertyBag,因为目前不打算支持基于表达式的排序条件:1: IEnumerablet select T(是规格ont规格,PropertyBag订单,sortOrder)2:其中T :类,新增();在Apworks .存储中,使用在哪里子句生成器BuildWhereClause子句子句方法,根据查询表达式表情生成在哪里子句,进而产生结构化查询语言语句并使用ADO .网访问关系型数据库:1:公共IEnumerablet select t(是规格上的规格,属性包订单,存储.SortOrder(排序顺序)2:其中T :类,new()3: { 4: } try 5: { 6: }表达式on count,bool表达式=null7:其中clausebuildresult其中buildresult=null8:字符串sql=字符串。格式(' SELECT {0} FROM {1} ',9: GetFieldNameListT(),GetTableNameT());10: if(规格!=null)11: {12:表达式=规范GetExpression();13:其中生成结果=getwhere clausebuildert().BuildWhereClause子句子句(表达式);14: SQL=' WHERE ' WHERE生成结果.在哪里子句;15: }16: if(订单!=null sortOrder!=存储排序顺序。未指定)17: { 18: SQL=' ORDER BY ' GetOrderByFieldListT(orders);19:交换机(sortOrder)20: {21:存储箱排序顺序。升序:22: SQL=' ASC23:断;24:箱子存放处。排序顺序。派生:25: SQL=" desc ";26:断;27:默认值break28: }29: }30:使用(DBcommand command=CreateCommand(SQL))31: { 32: if(command .连接==null)33:命令。连接=连接;34:如果(交易!=null)35:命令。交易=交易;36: if(规格!=null)37: { 38: }命令。参数。清除();39: var参数=getselectcriteriadbparametersist(其中生成结果.参数值);40: foreach(参数中的定义变量参数)41: { 42: }命令。参数。添加(参数);43: } 44: } 45: DBDataReader reader=command .ExecuteReader();46: LiST ret=new LiST();47:而(阅读器Read())48: {49: ret .add(CreateFromReaderT(reader));50: }51:阅读器close();//非常重要:阅读器必须关闭!52:返回ret 53: } 54: } 55: catch(expression parseeexception)56: { 57: } throw;58: }59:接球(基础设施除外)60: {61:抛投;62: } 63:捕获(异常ex)64: { 65:抛出异常管理器.handleexceptiondrithrowstorageexception(例如,66:参考资料EX_SELECT_FROM_STORAGE_FAIL,67:类型(T)10 .AssemblyQualifiedName,68:规格!=null?规格ToString() :“空”,69:订单!=null?命令. ToString() : 'NULL ',70:排序顺序);71: }72: }73: 下面这个方法将根据聚合根的类型与身份证,返回与之相关的所有域事件:1:公共虚拟IEnumeraleidomainevent加载事件(类型aggregateRootType,长id)2: { 3: try 4: { 5: 6: property bag sort=new property bag();7:排序。添加排序长('版本');8: var aggregateRootType name=aggregateRootType .AssemblyQualifiedName9:是规范域事件数据对象规范=specificationdomaineventdataobject 10:eval(p=p . AggregateRootId==id p . aggregaterootTYPe==aggregaterootTYPe名称);11: 12:返回SelectDomainEventDataObject(规范、排序Apworks .存储。排序顺序。升序)13:select(p=p . to entity());14: }15:接球{抛投;}16: }

版权声明:怎么用查询表达式表情建立查询对象模式是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。