你的位置:首页 > ASP.net教程

[ASP.net教程]应用程序框架实战三十六:CRUD实战演练介绍


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

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

实战演练介绍

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

  实战演练第一部分介绍如何快速解决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/

 




去浙江的旅游团价格去浙江旅游必去景点去浙江旅游要多少钱去浙江旅游最佳路线去浙江旅游最佳时间狼牙山门票_狼牙山旅游门票价格_狼牙山门票多少钱 张家界武陵源黄龙洞门票价格_杭州黄龙洞门票价格 海岛旅游攻略 太姥山在哪里_福建太姥山怎么走_太姥山门票及开放时间 世园会绿色农产品严把质量安全关 首个中国旅游日积极关注_积极参与_积极分享 原全国人大常委会副委员长蒋正华参观世园会 爱到咬下你的睫毛 昌乐到富华游乐园怎么走?昌乐到潍坊富华怎么坐车? 上海大观园介绍?上海大观园里面景点有什么? 上海大观园一日游最佳旅游路线?上海大观园几月份最好玩? 威海到潍坊富华游乐园怎么走?威海到富华游乐园怎么坐车? 2015佛山三水荷花世界圣诞节活动?三水荷花世界圣诞平安夜好玩吗? 行摄发现美 感悟大同苍凉之美 2015东莞华南mall欢乐天地圣诞节活动?华南欢乐天地圣诞平安夜好玩吗? 2015珠海横琴海洋乐园圣诞节活动?圣诞节横琴海洋乐园优惠吗? IXFC30N60P Datasheet IXFC30N60P Datasheet IXKC15N60C5 Datasheet IXKC15N60C5 Datasheet IXFR30N60P Datasheet IXFR30N60P Datasheet 广州大学有几个校区 广州大学有几个校区 广州大学有几个校区 百万葵园一日游 百万葵园一日游 百万葵园一日游 桂林到乐满地 桂林到乐满地 桂林到乐满地