从本篇开始,本系列将进入实战演练阶段。

  前面主要介绍了一些应用程序框架的概念和基类,本来想把所有概念介绍完,再把框架内部实现都讲完了,再进入实战,这样可以让初学者基础牢靠。不过我的精力很有限,文章进度越来越慢,所以准备切换一下介绍顺序,把实战演练提前,以方便你阅读代码。

实战演练介绍

  本系列实战演练共分两个部分。

  实战演练第一部分介绍如何快速解决CRUD机械操作,这一部分我将手把手带领各位同学从搭建VS环境开始,创建程序集及各程序集间的依赖关系,以及引入依赖的外部DLL,并手工完成代码示例中Application的三种界面操作。当你熟悉了手工操作方式后,你会发现这些工作枯燥乏味,效率低下,且容易出错。为解决该问题,我将为你介绍PowerDesigner(PD)和CodeSmith两大工具,分享PD及数据建模技巧,并发布配套的CodeSmith模板,你将体验到高质量完成机械代码的最佳实践。

  实战演练第一部分介绍的内容很有用,它将帮助你完成大量体力活,但这个示例太简单,体现不出领域模型的威力,实战演练第二部分以权限模块为例,演示如何开发具有一定业务逻辑的模块。

  当然,在介绍实战演练第二部分之前,我需要先把框架内部重要代码讲解完,本系列大致构成如下:

  1. 应用程序框架介绍
  2. 实战演练1:CRUD开发
  3. 框架代码详解
  4. 实战演练2:权限开发

示例代码阅读建议

  很多同学反映,阅读我的示例代码非常困难,经过了解,我发现大多是阅读方法有问题,在此给出一些建议。

  对于别人的代码,你阅读起来很痛苦是正常现象,因为编程习惯和风格不同,另外一个原因是对代码的意图不了解。

  我提供了Managements和Util两个VS解决方案,Managements是简单管理系统的代码示例,Util是框架代码,它是基础设施层的一部分,被分离出来的原因是让业务项目更简单、编译更快。

  阅读代码需要从简单的东西入手,这里就是Managements解决方案,它里面包含了一个叫Application的CRUD操作,它是权限系统中的应用程序模块,具体功能先不要考虑。Application模块是一个单表操作,非常简单,几乎没有业务逻辑,很适合用来入门。EasyUi提供了三种数据录入方式,即表单操作、表格操作、行内操作,我使用Application模块演示了这几种操作方式。

  有些同学下载代码后,直接看的Util代码,它主要包含一些基类和公共操作类,看起来困难就在所难免了。

  为了减少程序集数量,我把一些第三方开源框架的代码直接放入Util解决方案中,有些同学直接就看到这里面去了,然后告诉我异常复杂。不要泄气,说明你是一个正常人,对于第三方开源框架的代码,我也害怕,嘿嘿。我的代码风格很容易辨认,就是每个方法都具有中文注释,当你发现代码没有注释或注释全是英文,那一定不是我写的,我的英文水平很菜。

  当你把Application看熟以后,可以自己动手创建与Application类似的表,并手工完成三种CRUD操作。

  当你对CRUD相关的类和配置熟悉之后,就可以查看基类实现,这时候带着问题去看Util代码,会容易理解得多。

  下一步就是把这些代码逐步移植到你自己的项目中,只有把它们变成你的东西,才能发挥更大作用。这也是我写这个系列的目的,不仅授之以鱼,更要授之以渔。

  建议你至少能够扩展之后,才把我的东西用到你的项目上,不然坑很多,风险高。

CRUD概述

  CRUD是Create、Retrieve(Read)、Update、Delete的缩写,中文名:增删改查。不论哪家的应用框架,都特别关照它几兄弟,为什么?

  对于一般的中小型项目,业务逻辑复杂的模块只占很小的比例,一半以上的模块都比较简单。这些简单模块大致会通过一个界面或设备接口把数据收集上来,基本不经过中间处理(业务逻辑),直接存入数据库,在有需要的时候会把这些数据展示出来,或者为复杂模块提供基础支持。

  这些简单模块工作量大,技术含量低,通过手工的方式编写效率低下。

  当采用了分层架构,特别是DDD分层架构之后,更是雪上加霜。

  对于采用了DDD这种复杂分层架构,哪怕业务逻辑很复杂,还是存在不少机械工作,主要是创建各层的构造类型,比如领域实体及属性、DTO及属性映射、EF映射等,这些工作是必须的,但很枯燥乏味。

  可以看到,只要是信息系统,不论简单还是复杂,都存在机械工作,不同性质的项目机械工作所占比例不一样而已。

  后面我将用CRUD来指代开发中碰到的一切机械工作。

  对于机械工作,最好的办法是依靠生成器自动创建代码,在讨论生成器之前,先讨论下EF相关的概念。

混乱的EF概念

  EF在刚出土的时候,提供一个叫实体数据模型的edmx文件,打开这个文件,发现它是一个可视化类图设计器。

  在新建edmx文件时,有“从数据库生成”和“空模板”两个选项。

  如果你选择“从数据库生成”,说明你自己先创建了数据库,再通过edmx的反向生成工具生成代码,这就是所谓的db first,first是先行或优先的意思,db first就是数据库先行,先创建数据库,再读取数据库的元数据,生成代码。

  如果你选择了“空模板”,你可以在edmx可视化类图设计器中创建一些类和关联,edmx会自动帮你生成代码和数据库。这种方式称为model first,即模型先行,先有模型,后面再创建代码和数据库。

  代码生成出来后,你会发现这些代码文件被包含在edmx文件中,包括领域实体和DbContext工作单元,还有一些T4模板,这有什么影响?

  如果采用DDD分层架构,领域实体属于领域层,而DbContext属于基础设施层,放到一起会导致高耦合以及分层不清,这是edmx的主要问题。另外一堆不相干的代码生成模板与领域实体放到一起,估计也让你看着心烦。

  EF后续推出了更加轻量的使用方式,让你可以抛弃edmx文件,直接使用原生的DbContext,并支持了code first开发模式。code first即代码先行,先写代码,再自动创建数据库。code first开发模式能够真正实现持久化无关,从而设计出更加纯净的领域模型,特别在采用TDD开发时,更加威猛。

  从上面可以看出,edmx和原生DbContext是两种不同的EF技术,而db first、model first、code first则是不同的开发模式,但这些术语非常混乱,不同的人说同一个术语时可能指的是不同的东西。

  经常听到有人说他用的是code first,但实际上他的开发方式是先创建数据库,再生成代码,这属于db first,他用code first指代原生DbContext技术。

  还有一些人害怕使用EF,因为他认为原生DbContext只能使用code first开发模式,而他想采用db first方式,但他又不喜欢edmx。

  我用原生DbContext这个词的意思是,单独使用DbContext这个基类,因为edmx也使用的是DbContext,以示区别。

  下面用一个图来总结一下,如果说得不正确,请各位同学批评指正。

Code First还是Db First

  从上面分析得知,edmx不适合DDD分层架构,所以我们在EF技术上采用原生的DbContext,这个没有什么疑问了。那么开发模式是否一定要采用code first呢?

  前面说了,code first可以获得更纯的领域模型,但你见过炉火纯青的领域模型长什么样吗?对于DDD架构初学者,在很长时间都难领悟到它的精髓,所以不论你以code first还是db first,其结果没有显著不同。

  其次,.net大部分项目都是中小项目,且不太复杂,CRUD机械工作占很大篇幅,使用code first手工敲代码,效率十分低下,且工作量与表中的字段数量成正比。在配合TDD的情况下,才可以和生成的代码质量媲美,否则BUG依旧。

  可以看到,虽然code first万众瞩目,但却只有DDD高手开发很复杂的业务才能真正发挥威力。很复杂的业务需求,可能逻辑非常复杂,仅简单搜集数据字段,并不能很好的完成任务,这种场景基于DDD进行行为建模并配合TDD推进项目更有保障。

  根据我的项目实际情况,我采用了db first第一步用PD数据建模,第二步用PD生成建库脚本创建数据库,第三步采用CodeSmith生成代码,第四步选择性的复制代码并建立领域模型。

代码生成器介绍

  很多同学一听代码生成器,就会问哪种生成器最好,还有些同学则干脆自己开发,毕竟大家都是程序员,要开发个生成器软件有何难。

  技术人员总是对技术本身比较感兴趣,容易忽略做一件事的真正目的。其实对于代码生成器来说,真正重要的不是生成器软件,而是你需要获得的最终代码,它是由你的模板决定的。

  要创建出一套高质量的模板,关键是不断提纯自己的代码,把重复的代码尽量提取到基类。

  对于采用哪种生成器软件,根据自己的熟悉程度和喜好进行选择,我采用的是CodeSmith

  CodeSmith是一个收费的代码生成器,不过大家都使用绿色环保版本。使用它的原因是功能比较强大,能够与VS进行集成,编写模板时具有代码提示,类似ASPX语法,学习成本低,另外官方提供了一套EF DbContext模板,我们只需要简单修改,就可以用于实际开发中。

  需要创建哪些部分的代码呢?在最理想的情况下,所有机械代码全部生成,这样你可以在最短时间内拿下大部分机械工作,为你能够集中火力完成核心功能奠定基础。

  下面讨论几个与生成器相关的问题。

代码生成器与数据建模的关系

  一般的代码生成器都是通过读取数据库元数据来生成代码。

  如何评价生成的代码质量高低?

  第一个特征,所有代码是否具有准确的注释。

  大部分程序员都不喜欢写注释,不知道是因为打字慢,还是觉得没必要。哪怕你英文很牛X,你的命名非常标准,但你不能保证看你代码的人具有同样的英文水平。何况大部分人的英文还是和我一样菜,命名十分晦涩。在这种情况下,不要说给别人留条活路,那是给自己将来留的。

  如果你采用code first模式,手工编写所有代码,相信能给全套代码写全注释的人不多,每个领域实体的属性头上都要加上注释,而且还有大量相似类,比如Dto,不复制代码很难做到。

  第二个特征是自动帮你生成EF导航属性及相关映射配置,这是通过读取外键关系来创建的。

  很多.Net程序员不知道数据建模的价值,如果你问我一个项目里,哪种文档最重要,我会毫不犹豫的告诉你——数据建模文档。

  为什么数据建模这么重要,如果你现在接手一个遗留系统,你最需要什么?需求文档?类图?序列图?需求你不懂,还可以找用户问,但数据库中一个命名很晦涩的列,你要猜出它是什么意思,则难如登天。而类图和序列图等UML建模,主要是前期帮助理解和设计领域模型,不一定能够与代码同步更新,另外也不可能对每一个模块创建UML,完全没有必要。

  通过PowerDesigner进行数据建模,你可以让系统清晰度上升几个层次,让你看清表之间的关系,以及每个列的具体含义

  你可以在数据建模时,把每个列的注释加上,用PD创建数据库后,生成的代码中就具有良好的注释了。

  当你在表之间用关联线一拖,外键关系就建立了,生成的代码就具有了导航属性

  当然你可以直接在数据库中创建表,并添加注释,并手工创建外键关系。但这并没有让你减轻工作量,反而工作量更大,使用数据建模,工作轻松高效,且对项目未来维护有深远影响。

  生成高质量代码,除了你的模板外,另一个影响它的就是数据建模。

  我将在后面几篇分享我整理的CodeSmith模板,对于简单的CRUD操作,它可以生成全套代码,代码质量与我手工编写无异。

是否批量生成代码

  对于从三层架构过来的朋友,很多都用过代码生成器。

  如果系统有100个表,他们会把这100个表先建好,然后一次生成出来,然后再花几小时到几天的时间来整理修改。

  这个开发效率看上去很诱人,对于比较简单的三层架构和SQL操作可能是有效的,但对于EF+DDD分层架构却不太吃香。

  对于EF的导航属性,生成出来都是双向导航,但为了降低复杂度,可能会手工调整为单向导航,这时候也需要手工修改映射代码。

  EF操作,我总是保持小步前进,前进太快,出现任何一个问题,都可能浪费更多时间。很多时候看EF异常提示很难定位到问题,甚至断点调试也不起作用。这种情况下,最好的办法就是小步走,一出问题就可以迅速解决。

  用代码生成器创建DDD分层架构,一个弊端是导致一个表对应一个聚合,每个表都有一个仓储,这把你又带回了三层架构时代。不过对于新手来说,这没有多大问题,每个人都有一个成长的过程,第一步把充血模型用起来就行了,下一步再考虑聚合。

  但对于达到一定经验的人,直接用生成的代码就不合适了,因为聚合是DDD分层架构的核心,聚合使用得好,能显著降低系统复杂性,并使业务逻辑更好的内聚。

  所以如果你具备一定经验以后,不应该完全采用代码生成的老方式,更不能偷懒。应该选择性的复制代码手工组织聚合结构,这样一来,很多生成的代码都不需要了,比如某个仓储操作的是聚合内部实体,系统复杂性会大幅降低。

  我的方法是,按依赖顺序手工复制需要的代码,按聚合粒度复制并组织代码,一次操作一个聚合,把界面运行通过后再复制下一个。

  这样可以让你用db first模式开发出较高质量的领域模型,当然质量高低与水平成正比。

  对于很简单的CRUD模块,大多都是单表结构,这种情况下,一个表本来就是一个聚合,代码直接COPY,你的主要工作是调整下界面。

  对于比较复杂的模块,根据自己的理解手工复制代码组织聚合,生成的代码一般都达不到要求,比如界面布局比较复杂,这时候你会发现,生成的代码主要用于填充内容,你自己完成布局等功能。

  更复杂的模块,可以先不生成代码,用TDD推进并模拟出业务逻辑后,再进行数据建模生成代码,并复制需要的文件。

是否把代码生成器嵌入生产项目

  对于强大一点的生成器,都能够嵌入VS,并一键生成。这个特性也很诱人,如果把生成器嵌入生产项目,就不需要COPY文件了,这看起来能够极大的提升开发效率。

  与上一个问题一样,当你把生成器嵌入生产项目,生成的所有文件都进入你的项目,不论你需不需要它,这导致每个表一个仓储,增加了复杂性。

  我的方法是,把代码生成器与生产项目分离,手工复制相关文件,虽然看上去效率低,但可以根据需要选择代码和重新组织代码,质量将高得多。

数据库增加一个字段也要生成一下吗

  采用代码生成器的一个问题是,每当数据库增加一个字段,代码上相关的位置都要同步更新,很多懒汉希望通过重新生成并全面覆盖来解决这个问题。

  我的方法是仅在第一次生成全套代码,后面通过手工添加相关属性,如果增加的字段比较多,我可能先生成代码再手工将差异属性复制过去。原因很简单,项目上的代码不是完全生成的,有修改过的地方,重新生成并完全替换,可能会覆盖已修改代码。

一键生成简单代码,还是配置生成智能代码

  很多人在生成器下了大量功夫,能够支持复杂的配置,以生成出非常智能的代码。

  这可能造成对代码生成器的高度依赖,我仅使用代码生成器解决机械的简单工作,对于更智能的手工完成

  我的方法是一键生成简单代码。

分层构造元素是否可以简化

  有人看见生成的代码中,很多类都直接从基类派生,里面完全是空的,是否可以简化掉。

  这些类中啥也没有的原因是,基础操作已抽象到基类,由于没有什么业务逻辑,所以是空的。

  一般来说不能简化,因为对于稍复杂的模块,都需要往这些类中添加内容,如果没有它们你的代码将变得混乱,这些构造很好的组织了代码。

  除非你能确定你的项目基本都是CRUD,这种情况下确实可以简化,而且最好的办法是采用单层架构,单层架构在高度抽象和采用代码生成器的情况下,开发效率犹如火箭直冲宵汉。

也谈加班

  这两天园子里讨论加班的很多,我也说几句。

  不加班有几个条件:

  1. 计划合理
  2. 应用框架强大
  3. 开发人员平均水平较高

  第一点最困难,哪怕你们开发人员水平再高,框架也很强大,如果老板要求你2个月完成8个月的工作,你不加班是不可能的。

  如果计划合理,框架很强大,你一天用半天时间来开发,半天时间休息都绰绰有余。

结束语

本文分享了我在EF和代码生成器上的一些看法,不见得正确,那只是我摸索的一些经验,你应该找出最合适你们团队和项目的方法,并持续改进。

.Net应用程序框架交流QQ群: 386092459,欢迎有兴趣的朋友加入讨论。

.Net Easyui开发交流QQ群(本群仅限Easyui开发者,非Easyui开发者勿进):157809322

谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/xiadao521/

转载于:https://www.cnblogs.com/xiadao521/p/4440392.html

应用程序框架实战三十六:CRUD实战演练介绍相关推荐

  1. 应用程序框架实战二十六:查询对象

    信息系统的查询需求千变万化,在仓储中为每个查询需求创建一个特殊方法,将导致大量乏味而臃肿的接口. 一种更加可行的办法是,在应用层服务中描述查询需求,并通过仓储执行查询. 为了能够更好的描述查询需求,可 ...

  2. 应用程序框架实战二十二 : DDD分层架构之仓储(层超类型基础篇)

    前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能. 仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合.仓储中有很多操作都是可以通用的,可以把这部分操作抽取到 ...

  3. 奋斗吧,程序员——第三十六章 落花人独立,微雨燕双飞

    什么什么,要我别倒下? 废话,不看着情敌们在我面前一个个倒下,然后踩着他们的尸体过去继续战斗,我怎么可能比他们先一刻倒下. 生命的意义在于不屈的抗争,只要情敌一天未清除,我们就不能退出战斗. 我们必须 ...

  4. 疯狂的程序员-第三十六章

    一干人各自收拾了自己的东西来到周总办公室,燕儿还没在公司开过会,特别谨慎,专门备了笔记本和笔. 周总的桌子上乱糟糟地摆满了资料,烟灰被风从烟缸里吹出来飘得满地都是.他耸耸鼻子慢吞吞地说:"半 ...

  5. wepy组件子父传值_【WePY小程序框架实战三】-组件传值

    父子组件传值 静态传值 静态传值为父组件向子组件传递常量数据,因此只能传递String字符串类型. 父组件 (parent.wpy) 子组件(child.wpy) {{name}} props={ n ...

  6. 微信小程序把玩(三十六)Storage API

    其实这个存储在新建Demo的时候就已经用到了就是存储就是那个logs日志,数据存储主要分为同步和异步 异步存储方法: 存数据 wx.setStorage(object) 相同key会覆盖,可写回调方法 ...

  7. android仿秒拍源码,你所不知道的程序员 程序员其实真的很…【Bus Weekly】三十六期...

    原标题:你所不知道的程序员 程序员其实真的很-[Bus Weekly]三十六期 快,点击蓝色"字体"关注这个公众号,一起涨姿势- 现如今,程序员在中国的科技 圈可以说已经达到了举足 ...

  8. 程序员编程艺术第三十六~三十七章、搜索智能提示suggestion,附近点搜索

    第三十六~三十七章.搜索智能提示suggestion,附近地点搜索 作者:July.致谢:caopengcs.胡果果. 时间:二零一三年九月七日. 题记 写博的近三年,整理了太多太多的笔试面试题,如微 ...

  9. [Python从零到壹] 三十六.图像处理基础篇之图像算术与逻辑运算详解

    欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...

最新文章

  1. c3p0连接池配置_Maven+JSP+Servlet+C3P0+Mysql实现的音乐库管理系统
  2. ORACLE中的imp和exp
  3. MyBatis01 Idea中搭建MyBatis开发环境
  4. 一次bug调试经验----To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
  5. linux出站入站端口维护,linux下如何用iptables开放指定端口_网站服务器运行维护,linux,iptables,端口...
  6. 接口 java性能_接口测试性能测试
  7. r语言kmodes_聚类分析——k-means算法及R语言实现
  8. [今日白学]组件的基础的基础的基础
  9. 判断输入几位数的正则_判断是几位数,并逆向输出此数的程序算法和说明
  10. JDK和CGLIB生成动态代理类的区别
  11. NAS网络存储使用教程之如何新建用户
  12. ⚡自组织映射(SOM)神经网络⚡Python实现 |Python技能树征题
  13. Proteus器件查找
  14. cups ipp oracle,使用 CUPS 打印管理器管理打印机
  15. win7虚拟机VCMI无法自动安装驱动程序
  16. 那些看网络小说的时光——2019
  17. 4.15 使用渐变映射命令制作艺术效果 [原创Ps教程]
  18. vue用watch监听属性变化
  19. 别人都在这个春暖花开的端午节吃粽子,而我在踏青学springboot数据持久化
  20. chmod授权sh文件执行权限

热门文章

  1. 命令点无效怎么处理_怎么更好处理闲置包包,买包卖包都要记住这5点
  2. 服务器存储系统技术方案,服务器存储技术方案.pdf
  3. angularjs 让当前路由重新加载_Spring Cloud Gateway的动态路由怎样做?集成Nacos实现很简单...
  4. C中的危险函数(缓冲区溢出)
  5. Redis源码剖析(二)io多路复用函数及事件驱动流程
  6. 1029 旧键盘 (20 分)(c语言)
  7. 1017 A除以B (20 分)(c语言)
  8. ansible(2)——基本命令
  9. 防火墙(4)——iptables(2)增加修改规则-A -R
  10. CentOS搭建FTP服务器