循序渐进开发web项目操作说明书


广州爱奇迪软件科技有限公司( http://www.iqidi.com) Mail:wuhuacong@163.com 循序渐进开发 Web 项目 操作说明书 版本:2.0 编制人:伍华聪 广州爱奇迪软件科技有限公司( http://www.iqidi.com) Mail:wuhuacong@163.com 序号 修改人 修改日期 修改后版本 修改说明 1 伍华聪 2013-09-30 V1.0 正式版 2 伍华聪 2014-07-02 V2.0 根据最新功能进行完善 3 伍华聪 2014-12-03 19:49:20 V3.0 增加存储过程、Web 项目设计分析等 文件名称:循序渐进开发 Web 项目操作说明书 第 1 页 共 65 页 目 录 1. 引言 ............................................................................................. 3 1.1. 背景 ............................................................................................................. 3 1.2. 编写目的 ..................................................................................................... 4 1.3. 参考资料 ..................................................................................................... 4 1.4. 术语缩写及约定 ......................................................................................... 4 2. 基础模块的设计分析 .................................................................. 4 2.1. 数据库表设计 ............................................................................................. 4 2.2. 项目框架的生成 ......................................................................................... 6 2.3. 项目实体层代码分析 .................................................................................. 7 2.4. 数据访问接口的定义 .................................................................................. 9 2.5. 数据访问接口实现类的定义 .....................................................................10 2.6. 业务逻辑层的实现分析 .............................................................................14 3. 存储过程的使用 ........................................................................ 16 3.1. 存储过程的框架支持 .................................................................................17 3.2. SQLSERVER 存储过程 ................................................................................19 3.2.1. SQLServer 存储过程的编写 .........................................................19 3.2.2. SQLServer 存储过程的使用 .........................................................20 3.3. ORACLE 存储过程 ......................................................................................23 3.3.1. Oracle 存储过程的编写 ................................................................23 3.3.2. Oracle 存储过程的使用 ................................................................25 4. WEB 项目的设计分析.............................................................. 27 4.1. MVC 的 WEB 架构说明 .............................................................................27 文件名称:循序渐进开发 Web 项目操作说明书 第 2 页 共 65 页 4.1.1. MVC 的项目目录说明 .................................................................28 4.1.2. MVC 的控制器设计 .....................................................................30 4.2. WEB 项目代码的生成操作 ........................................................................34 4.2.1. 核心逻辑代码生成 .......................................................................34 4.2.2. Web 界面层代码生成 ...................................................................35 4.3. 几种界面控件的使用 .................................................................................38 4.3.1. 单行文本框 ...................................................................................38 4.3.2. 多行文本框 ...................................................................................38 4.3.3. 日期输入控件 ...............................................................................39 4.3.4. 数值输入控件 ...............................................................................40 4.3.5. 标签控件 ......................................................................................41 4.3.6. 下拉列表框 ...................................................................................41 4.3.7. 下拉列表树控件 ...........................................................................43 4.3.8. 树控件 ..........................................................................................44 4.3.9. 表格控件 DataGrid .......................................................................46 4.3.10. 布局控件 ......................................................................................51 4.3.11. 对话框控件 ...................................................................................52 4.3.12. 提示信息控件 ...............................................................................53 5. WEB 框架的架构特点.............................................................. 56 5.1. 技术特点 ....................................................................................................56 5.2. 代码生成工具 DATABASE2SHARP 的整合...................................................59 5.3. 基于多数据库的数据查询模块和通用查询模块 ......................................60 5.4. 框架提供基于多种数据库的整合 .............................................................62 文件名称:循序渐进开发 Web 项目操作说明书 第 3 页 共 65 页 1. 引言 1.1. 背景 前几年,我一直在做 Web 项目,使用 ASP.NET 的 WebForm 方式开发了几个较为大型的行 业管理系统,经过不断的完善和技术积累,也逐渐形成了一个标准的信息管理系统的 Web 开发框架,这个是早期技术的 Web 开发框架,包含了权限管理模块、菜单管理模块、字典管 理以及行业管理业务模块等,同时开发了一些 Web 的相关控件,包括 Web 分页控件、Web 查 询控件、Web 文件上传等控件,同时也已经结合代码生成工具 Database2Sharp 进行大部分 的代码生成,包括 Web 前端的一些界面。 在原先的 Web 框架里面,主要是采用 FrameSet 的原始方式进行布局,很多内容依靠 Javascript 类库进行操作,小部分采用了 EasyUI 的一些特性,总体来说,是比较传统的一 种框架模式,这个框架里面我已经集成了用户角色等权限方面的管理和控制、菜单管理、字 典管理、业务流程审批管理等模块,因此对开发常规的行业应用有着比较快的开发效率,不 过缺点也比较明显,就是在多浏览器支持方面,没有做的很好,框架里面采用的布局、样式 及技术等方面不够统一,不够新颖,但即使这样,这套框架也顺利用来开发了几套很大规模 的行业应用系统了。 随着 Web 前端技术的发展,MVC 的技术也已经渐趋成熟,JQuery 等应用技术已经是遍地 开花,应用非常广泛,有很多组件模块可以复用,界面的美观以及易用方面都有很大的提高。 为了引入更新、更强大的 Web 技术,我对 Web 开发框架整体进行了改造,使用基于 MVC + EasyUI + Enterprise Library 等相关的技术对原有的 Web 开发框架进行了升级,并引入了 功能强大且流行很广的 EasyUI 界面组件,以及 Uploadify 文件上传组件、LODOP 打印组件、 CKEditor 富文本编辑控件、Tags-Input 标签录入控件、HighCharts 图表展示控件、 Word/Excel 文档导出组件等,对《Web 开发框架》整体进行加强和完善,该《Web 开发框架》 是我们经过多年的项目积累,吸收众多框架产品的前沿思路,以及客户的宝贵意见,反复提 炼优化而成的。 文件名称:循序渐进开发 Web 项目操作说明书 第 4 页 共 65 页 1.2. 编写目的 本文档主要介绍如何在《Web 开发框架》的基础上循序渐进进行项目的增量开发,并使 得开发工程师了解整个框架的各部分内容及关联关系,以便能够更全面的、更真实了解《Web 开发框架》的相关特点。 1.3. 参考资料 序号 名称 版本/日期 来源 1 《Web 开发框架-架构设计说明书.doc》 内部 2 《Winform 开发框架-架构设计说明书.doc》 内部 3 内部 1.4. 术语缩写及约定 1 在本文件中出现的“Web开发框架”一词,除非特别说明,均指基于MVC + EasyUI + Enterprise Library等相关的技术研制的最新Web开发框架。 2 在本文安装.NET框架中,除非特别说明,均指 .NET 4.5 框架。 3 本文的开发环境为Visual Studio 2013,基于 MVC5 的Web开发技术。 2. 基础模块的设计分析 2.1. 数据库表设计 俗话说万层高楼从底起,开发应用项目,数据库的设计很重要,它可能是业务对象,业 务流程的综合设计,好的数据库设计可以减少后期的重复返工,提高开发效率。 我们以一个简单的数据库表进行设计讨论,一步步分析其中的关系。 文件名称:循序渐进开发 Web 项目操作说明书 第 5 页 共 65 页 1)表和字段名称 一般表名称,根据不同的业务关系,我们可以使用不同的前缀进行区分,使用前缀,可 以非常方便区分不同的业务表,如我自己一般基础表使用 “TB_” 定义前缀,权限系统表使 用"T_ACL_"定义前缀,工作流表使用"TBAPP_",业务表使用"T_"等,这样对于区分不同的 业务,方便管理很有好处。 字段名称方面,我们可以约定一些规则,如约定主键使用 ID;一般来说,ID 作为主键, 可以使用自增长的整形字段,也可以使用 GUID 的字符型字段,如果为了方便兼容不同的数 据库且方便迁移或者开发基于网络方面的应用,我建议还是使用 GUID 的字符型字段,使用 这种类型的字段,我们从创建数据的时候,就可以知道这个记录的主键,对于我们维护父子 表等关系非常有利。 字段的命名,建议以简单为主,如客户名称,直接使用 Name 来命名即可,不需要使用 CustomerName 这样啰嗦的名称,如多个单词组合的话命名采用 Camel 的格式。 由于如果采用字符型的 ID 主键,那么我们如果需要正确排序的时候,可能需要增加一 个 CreateTime 的日期类型,方便我们根据日期进行排序。 如果这个表还有一个外键的引用,建议统一命名标准,我一般使用 "表名称_ID" 这样 的名称,如 User_ID、Contact_ID 等相似的名称作为外键,不需要表的前缀。 2)数据库的模型设计 数据库的模型设计,我们建议在第三方的数据库设计工具上进行设计,如 PowerDesigner 这样的设计工具,使用工具设计数据库有很多好处,一个是可以高效率进行调整,二是根据 需要生成不同的数据库类型 Sql 语句,三是可以全局了解各个表之间的关系等等。使用 PowerDesigner 这样的数据库设计工具,能够在很大程度上提高我们数据库的设计效率。 如果要开发基于 Sqlite 的数据库应用,建议先在 SqlServer 上进行开发,然后在 DAL 层 文件名称:循序渐进开发 Web 项目操作说明书 第 6 页 共 65 页 增加一个 Sqlite 的数据访问层,并修改配置文件指向就可以了(基本上和 SqlServer 差不多)。 2.2. 项目框架的生成 设计好数据库后,我们通过代码生成工具进行整个项目框架的生成,这样对于我们在开 发新项目上有很好的好处,里面的项目层级、DLL 的 引用关系,已经处理好了,这样对我 们非常方便。不过大多数情况下,我们都是增量开发较多,也就是我们可能前面已经完成了 一些其他业务的开发,可能新增一个两个表,或者一批业务表的处理,这样也没关系,我们 把新生成的代码复制到项目即可,由于项目生成的时候,指定了主命名空间和相关的表前缀, 这样我们生成后的代码就方便阅读很多,减少累赘和出错的机会。 Web 开发框架(包括 Winform 项目),常见的分层模式,可以分为 UI 层、BLL 层、DAL 层、IDAL 层、Entity 层、公用类库层等等。 文件名称:循序渐进开发 Web 项目操作说明书 第 7 页 共 65 页 这个分层,在 Web 项目或者 WInform 项目(包括 WPF 项目),这些分层都是可以重用 的,这样我们就不用重复处理界面一下的逻辑,针对性的开发我们需要的界面层即可。 DAL 层根据不同的需要,扩展支持不同的数据库类型,每个数据库类型,对应一个数 据库访问实现层即可,它们实现 IDAL 层的接口,称之为数据库访问接口实现层。 2.3. 项目实体层代码分析 通过代码工具,我们已经可以完整生成基础的项目框架了,下面我们来分析下项目的源 码,从而知道整个框架的架构和代码的层次是如何的。 刚才我们看到,生成的项目里面,已经包含了实体类,我们以开篇介绍的一个表生成的 代码来进行研究分析。 文件名称:循序渐进开发 Web 项目操作说明书 第 8 页 共 65 页 其中我们看到下面的代码,里面使用了基类 BaseEntity,这个是所有生成的实体类的基 类,基类 BaseEntity 只是一个实体类的声明,没有什么属性,使用这个实体类基类,只是为 了整个框架更好管理和控制。BaseEntity 来源于公用类库,已经封装在里面了。 /// /// 客户信息 /// [DataContract] public class CustomerInfo : BaseEntity { 另外,我们看到,实体类有注释,这些注释来自数据库的备注信息,包括字段的注释也 是来自数据库的备注说明信息。 还有类的定义里面,还看到了[DataContract] 的标签,以及类的属性[DataMember], 这个是 WCF 技术里面传输数据的协议声明,我们目前开发的应用,一般都是基于.NET4.0 的了,因此包含这个属性方便我们在开发网络版项目的时候用到,一般情况下忽略即可。 我们继续看看实体类的其他部分代码: #region Field Members private string m_ID = System.Guid.NewGuid().ToString(); //编号 private string m_Name; //姓名 private int m_Age = 0; //年龄 private string m_Creator; //创建人 private DateTime m_CreateTime; //创建时间 #endregion 我们看到,对于字符型的 ID 主键字段,代码生成的时候,已经自动添加默认属性值 (GUID:System.Guid.NewGuid().ToString() )的了,这样我们创建实体类的时候,这个 ID 的 值就已经生成了。 文件名称:循序渐进开发 Web 项目操作说明书 第 9 页 共 65 页 2.4. 数据访问接口的定义 上面我们分析了实体类的定义,本节继续分析其他部分的内容,如数据访问接口成的定 义如下所示。 namespace WHC.TestProject.IDAL { /// /// 客户信息 /// public interface ICustomer : IBaseDAL { } } 这里面的代码很简单,没有多余的代码行,那么里面究竟发生了什么呢,其中的 IBaseDAL 又是什么定义呢? 其实,IBaseDAL 就是定义了很多我们开发用到的基础接口,如标准的增删改查,以及 衍生出来的一些其他接口,如分页查询,条件查询等接口内容。这个 ICustomer 就是用来定 义一些除了标准接口不能实现外的业务接口。 IBaseDAL 通过传入一个实体类,从而方便给基类接口提供强类型的数据类型指定,提 高我们的开发效率,减少出错的机会。我们可以在 VS 里面查看 IBaseDAL 的定义,如下所 示: 文件名称:循序渐进开发 Web 项目操作说明书 第 10 页 共 65 页 可以看到里面很多相关的接口定义,有返回实体 T 的,也有返回 List的,还有 DataTable 类型等等,这些基础接口,经过我们多个项目的应用实践,已逐步稳定并能够提 供很好的接口支持,方便我们快速调用处理。 即使我们在没有实现任何业务接口的情况下,仅仅利用标准的基类 API,也基本上能够 完成绝大多数的数据操作功能了。 2.5. 数据访问接口实现类的定义 我们分析完 IDAL 的数据访问接口成的定义后,继续了解一下,如何基于这个接口进行 访问层的实现设计的。数据访问的实现层在项目中的位置如下所示(以基于 SqlServer 的 DALSQL 层进行分析)。 文件名称:循序渐进开发 Web 项目操作说明书 第 11 页 共 65 页 它的类代码定义如下所示。 namespace WHC.TestProject.DALSQL { /// /// 客户信息 /// public class Customer : BaseDALSQL, ICustomer { 数据访问接口实现层和接口定义层一样,都有一个基类,如基于 SqlServer 实现的基类 为 BaseDALSQL,这个基于 SqlServer 的数据访问基类,它也是继承自一个超级基类(大多 数的实现在这里)AbstractBaseDAL。他们之间的继承关系如下所示。 文件名称:循序渐进开发 Web 项目操作说明书 第 12 页 共 65 页 而我们刚才在项目工程的图里面看到,BaseDALSQL、IBaseDAL、AbstractBaseDAL 这 些类库由于具有很大的通用性,为了减少在不同的项目中进行复制导致维护问题,因此我们 全部把这些经常使用到的基类或者接口,抽取到一个独立的类库里面,为了和普通的DotNET 公用类库命名进行区分( WHC.Framework.Commons ), 我 们 把 它 命 名 为 WHC.Framework.ControlUtil。 BaseDALSQL 基类的定义如下所示。 文件名称:循序渐进开发 Web 项目操作说明书 第 13 页 共 65 页 这样做的好处是,在所有的模块里面,避免复制导致的版本维护问题,同时也减少代码 的重复生成,增量生成的全部代码,可以一次性复制到整个项目工程里面,而不会导致基础 类库的替换,因为这些基类不在生成目录里面,所有生成的类文件,都是和业务表相关的, 如下所示。 具体的数据访问实现类(如 Customer),它把数据库信息转换为实体类,有一个函数, 在代码生成的时候已经生成;同时在把实体类的属性保存到数据库也有一个类似 CRM 的映 射关系,从而实现可空的字段获取和更新操作。 /// /// 将 DataReader 的属性值转化为实体类的属性值,返回实体类 /// /// 有效的 DataReader 对象 /// 实体类对象 protected override CustomerInfo DataReaderToEntity(IDataReader dataReader) { CustomerInfo info = new CustomerInfo(); SmartDataReader reader = new SmartDataReader(dataReader); info.ID = reader.GetString("ID"); info.Name = reader.GetString("Name"); info.Age = reader.GetInt32("Age"); info.Creator = reader.GetString("Creator"); info.CreateTime = reader.GetDateTime("CreateTime"); return info; } /// 文件名称:循序渐进开发 Web 项目操作说明书 第 14 页 共 65 页 /// 将实体对象的属性值转化为 Hashtable 对应的键值 /// /// 有效的实体对象 /// 包含键值映射的 Hashtable protected override Hashtable GetHashByEntity(CustomerInfo obj) { CustomerInfo info = obj as CustomerInfo; Hashtable hash = new Hashtable(); hash.Add("ID", info.ID); hash.Add("Name", info.Name); hash.Add("Age", info.Age); hash.Add("Creator", info.Creator); hash.Add("CreateTime", info.CreateTime); return hash; } 2.6. 业务逻辑层的实现分析 分析完成了数据访问层的接口和实现类后,我们来进一步看看业务逻辑层的实现分析, 由于数据访问层的本意是基于特定的数据库实现,因此业务逻辑层就是抽象不同的数据库, 让它们根据配置,指向不同的数据库实现类,从而实现多数据库的支持。 namespace WHC.TestProject.BLL { /// /// 客户信息 /// public class Customer : BaseBLL { public Customer() : base() { base.Init(this.GetType().FullName, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name); } } } 业务逻辑层的代码也很简单,在构造函数里面 Init 一下即可,之所以使用这个 Init 操作, 其实为了确定 BLL 层的业务对象名称和指定在哪个程序集里面进行构造的需要,让给基类 进行必要的创建工作。 文件名称:循序渐进开发 Web 项目操作说明书 第 15 页 共 65 页 在 BaseBLL 的 Init 函数里面,我们根据子类传入的相关参数,由于我们约定了数据访问 类的命名空间,因此只根据数据库配置的不同需要,替换部分名称,就可以具体的构造出一 个数据访问类了。 #region 根据不同的数据库类型,构造相应的 DAL 层 AppConfig config = new AppConfig(); string dbType = config.AppConfigGet("ComponentDbType"); if (string.IsNullOrEmpty(dbType)) { dbType = "sqlserver"; } dbType = dbType.ToLower(); string DALPrefix = ""; if (dbType == "sqlserver") { DALPrefix = "DALSQL."; } else if (dbType == "access") { DALPrefix = "DALAccess."; } else if (dbType == "oracle") { DALPrefix = "DALOracle."; } else if (dbType == "sqlite") { DALPrefix = "DALSQLite."; } else if (dbType == "mysql") { DALPrefix = "DALMySql."; } #endregion this.dalName = bllFullName.Replace(bllPrefix, DALPrefix);//替换中级的 BLL.为 DAL. 就是 DAL 类的全名 baseDal = Reflect>.Create(this.dalName, dalAssemblyName);//构造对应的 DAL 数据访问层的对象类 这样精确构造出来的数据库访问访问对象,并把它转换为基类接口,那么就可以在 文件名称:循序渐进开发 Web 项目操作说明书 第 16 页 共 65 页 BaseBLL 类里的基类接口进行调用了。而构造业务对象,通过 BLLFactory的泛型工厂, 更能够精确构造出对应的业务对象类,这样构造出来的对象具有强类型,非常方便使用。 以上就是业务逻辑层,数据访问层和数据访问接口层的设计关系,为了高效进行开发工 作,我们一定要使用强类型的接口调用,这样可以大大减少出错机会,而返回的基类接口, 由于传入了特定的具体类型 T,也能够构造出强类型的列表或者对象。因此,合理利用泛型, 能够是我们的开发体验更加美好,更加高效。 3. 存储过程的使用 在我前面很多篇关于框架设计和介绍的文章里面,大多数都是利用框架提供的基础性 API 进行各种的操作,包括增删改查、分页等各种实现和其衍生的实现,而这些实现绝大多 数是基于 SQL 的标准操作实现的,由于框架的底层是利用了微软企业库 Enterprise Library,因此框架也是很好的支持存储过程的各种调用,不过由于整体性和数据库迁移方 面的考虑,建议一般使用标准的 SQL 操作而已,这样能够很大程度上保证数据库可以很平 滑过渡到其他数据库,如 Access、SQLite 等单机版数据库。 但是,有时候我们提供对存储过程的支持也是十分必要的,有些业务可能就只是固定在 某种特定的数据库上跑,如 SQLServer、Oracle 等这些支持存储过程的关系型数据库,有 文件名称:循序渐进开发 Web 项目操作说明书 第 17 页 共 65 页 些业务可能还真的需要存储过程的整体性的封装;基于这个原因,我们在这小节对存储过程 的使用进行介绍。 虽然存储过程一般用于处理一些复杂的逻辑关系或者报表内容,不过为了介绍方便,我 们从几个较为基础的操作进行介绍。我们以一个客户表来进行对应的存储过程来介绍,先介 绍客户表 T_Customer 的表定义。 3.1. 存储过程的框架支持 前面介绍了,我们整个实例是以一个客户表 T_Customer 为例进行讲解的,整个表的 框架支持代码,可以通过代码生成工具进行快速生成,生成后包括了 IDAL、Entity、 DALSQL、BLL 层代码。框架在数据库的抽象基类 AbstractBaseDAL 里面,已经提供了 对上面各种存储过程的支持,这样各个数据访问层的子类都可以进行调用,实现存储过程的 处理。 对于存储过程的实现,我们分析一下各个接口,可以看到,输入参数是可选的,因为有 些接口不需要输出参数;输出参数也是可选的,有些接口也不需要输出参数,返回的记录类 型主要有 bool 类型,实体类型,实体集合类型,DataTable 类型这几种,当然虽然有年龄 接口的整形,但是这个是通过输出参数来获得的。我们于是可以定义一些类似这样的通用接 口参数集合,用来处理存储过程调用的封装接口。下面是在框架的数据库抽象基类 AbstractBaseDAL 里面定义好的通用存储过程接口。 #region 存储过程执行通用方法 /// /// 执行存储过程,如果影响记录数,返回True,否则为False,修改并输出外部参数outParameters(如 果有)。 /// /// 存储过程名称 /// 输入参数,可为空 /// 输出参数,可为空 文件名称:循序渐进开发 Web 项目操作说明书 第 18 页 共 65 页 /// 事务对象,可为空 /// 如果影响记录数,返回True,否则为False public bool StorePorcExecute(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null) /// /// 执行存储过程,返回实体列表集合,修改并输出外部参数outParameters(如果有)。 /// /// 存储过程名称 /// 输入参数,可为空 /// 输出参数,可为空 /// 事务对象,可为空 /// 返回实体列表集合 public List StorePorcToList(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null) /// /// 执行存储过程,返回DataTable集合,修改并输出外部参数outParameters(如果有)。 /// /// 存储过程名称 /// 输入参数,可为空 /// 输出参数,可为空 /// 事务对象,可为空 /// 返回DataTable集合 public DataTable StorePorcToDataTable(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null) /// /// 执行存储过程,返回实体对象,修改并输出外部参数outParameters(如果有)。 /// /// 存储过程名称 /// 输入参数,可为空 /// 输出参数,可为空 /// 事务对象,可为空 /// 返回实体对象 public T StorePorcToEntity(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null) #endregion 文件名称:循序渐进开发 Web 项目操作说明书 第 19 页 共 65 页 3.2. SQLServer 存储过程 3.2.1. SQLServer 存储过程的编写 1)提供执行处理,可对执行结果进行反馈 这种情况常常可以见到,如可以对插入、更新、删除等操作进行处理,并获得执行的结果, 下面是这两种存储过程的代码。 ------------------------------------ --功能描述:插入数据到表中 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_Insert ( @ID varchar(50), @Name varchar(50) , @Age int ) AS begin tran Insert into dbo.T_Customer( ID,Name,Age ) Values( @ID,@Name,@Age ) if @@error!=0 begin rollback end else begin commit end go 2)提供执行处理,获得一个或者多个返回性参数,并可对执行结果进行反馈。 基于上面的处理方式,我们可能还有一种情况,就是需要执行存储过程,并返回对应的返回 参数,我们可以在程序里面利用代码获取这些返回参数的数值,从而用作其他用途。 因此,这种操作,如要是获取返回性参数的情况,如下所示是判断记录是否存在,以及获取 客户最大年龄的两个存储过程。 ------------------------------------ --功能描述:以字段 ID 为关键字,检查表中是否存在符合条件的记录 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_ExistByID ( 文件名称:循序渐进开发 Web 项目操作说明书 第 20 页 共 65 页 @Exist int output , @ID varchar(50) ) AS Select @Exist = Case When Exists (Select 1 From dbo.T_Customer Where ID=@ID) Then 1 Else 0 End go 3)提供查询处理,并返回实体对象 这小节后面介绍的内容,都是存储过程的返回值,这些或者是一条记录,或者是多条记录的 查询结果,这个在 SQLServer 里面很容易实现,而在 Oracle 里面需要通过游标进行处理。 下面存储过程脚本,是基于返回单条记录的存储过程。 ------------------------------------ --功能描述:以字段 ID 为关键字,检索表中的数据 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_SelectByID ( @ID varchar(50) ) AS Select * from dbo.T_Customer Where ID= @ID go 4)提供查询处理,并返回多条记录集合;包括实体列表集合或 DataTable 集合对象 对于返回多条集合的对象,在存储过程里面体现都一样的,我们可能在 C#处理的时候, 把它转换为不同的对象即可,返回多个集合,在 SQLServer 里面,它们的存储过程代码如 下所示。 ------------------------------------ --功能描述:检索表中所有的数据 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_SelectAll AS Select * from dbo.T_Customer go 3.2.2. SQLServer 存储过程的使用 上面小节,介绍了在 SQLServer 里面,如何编写个各类存储过程,本小节主要介绍如 何在 C#里面,如何对这些存储过程进行调用,并获取到对应的数据类型,如输出参数,单 文件名称:循序渐进开发 Web 项目操作说明书 第 21 页 共 65 页 个数据记录,多个数据记录等情况。 对存储过程的使用,也还需要我们进行定义相应的实现,如编写存储过程并在数据库上 执行,然后在代码进行封装调用。我们可根据需要在 SQLServer 数据库上创建一些需要使 用的存储过程,如下图所示。 例如对于 SqlServer 数据访问层,使用超级基类的接口,我们简化代码如下所示。 public bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null) { Hashtable inParameters = new Hashtable(); inParameters.Add("ID", info.ID); inParameters.Add("Name", info.Name); inParameters.Add("Age", info.Age); return StorePorcExecute("T_Customer_Insert", inParameters, null, trans); } public bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null) { Hashtable inParameters = new Hashtable(); inParameters.Add("ID", info.ID); inParameters.Add("Name", info.Name); inParameters.Add("Age", info.Age); 文件名称:循序渐进开发 Web 项目操作说明书 第 22 页 共 65 页 return StorePorcExecute("T_Customer_UpdateByID", inParameters, null, trans); } public List StorePorc_GetAll(DbTransaction trans = null) { return StorePorcToList("T_Customer_SelectAll", null, null, trans); } public DataTable StorePorc_GetAllToDataTable(DbTransaction trans = null) { return StorePorcToDataTable("T_Customer_SelectAll", null, null, trans); } public CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null) { Hashtable inParameters = new Hashtable(); inParameters.Add("ID", ID); return StorePorcToEntity("T_Customer_SelectByID", inParameters, null, trans); } public bool StorePorc_ExistByID(string ID, DbTransaction trans = null) { Hashtable inParameters = new Hashtable(); inParameters.Add("ID", ID); Hashtable outParameters = new Hashtable(); outParameters.Add("Exist", 0); StorePorcExecute("T_Customer_ExistByID", inParameters, outParameters, trans); int exist = (int)outParameters["Exist"]; return exist > 0; } public bool StorePorc_DeleteByID(string ID, DbTransaction trans = null) { Hashtable inParameters = new Hashtable(); inParameters.Add("ID", ID); 文件名称:循序渐进开发 Web 项目操作说明书 第 23 页 共 65 页 return StorePorcExecute("T_Customer_DeleteByID", inParameters, null, trans); } public int StorePorc_GetMaxAge() { Hashtable outParameters = new Hashtable(); outParameters.Add("MaxAge", 0); StorePorcExecute("T_Customer_MaxAge", null, outParameters, null); int MaxAge = (int)outParameters["MaxAge"]; return MaxAge; } 3.3. Oracle 存储过程 3.3.1. Oracle 存储过程的编写 对应 SQLServer 的存储过程,Oracle 的存储过程也提供了对应的版本,下面是几种 情况下的 Oracle 存储过程的编写。 1)提供执行处理,可对执行结果进行反馈 ------------------------------------ --功能描述:插入数据到表中 ------------------------------------ Create Or Replace Procedure T_Customer_Insert ( p_ID IN T_CUSTOMER.ID%TYPE, p_Name IN T_CUSTOMER.NAME%TYPE, p_Age IN T_CUSTOMER.AGE%TYPE ) AS Begin Insert into T_CUSTOMER( ID,NAME,AGE ) Values( p_ID,p_Name,p_Age ) ; Commit; Exception When Others Then Rollback; End; 文件名称:循序渐进开发 Web 项目操作说明书 第 24 页 共 65 页 其中上面的代码涉及几个地方,T_CUSTOMER.ID%TYPE 是表示根据字段动态决定参 数的类型,避免应硬编码或者反复修改参数类型。Oracle 的参数一般使用 p_的前缀开始, 方便区分。 2)提供执行处理,获得一个或者多个返回性参数,并可对执行结果进行反馈。 ------------------------------------ --功能描述:以字段 ID 为关键字,检查表中是否存在符合条件的记录 ------------------------------------ Create Or Replace Procedure T_Customer_ExistByID ( p_Exist OUT Number , p_ID IN T_CUSTOMER.ID%TYPE ) AS Begin --V9.i 以下使用的语句 Select Case When (Count(1)>0) Then 1 Else 0 End Into p_Exist From T_CUSTOMER Where ID=p_ID ; --也可以使用的语句 -- Select Decode(Count(1),0,0,1) Into p_Exist From T_CUSTOMER Where ID=p_ID ; End; 上面的代码,都有一个输出的参数,虽然他们执行没有影响记录函数,但是这个主要是 通过输出参数的值进行处理了。 3)提供查询处理,并返回实体对象 提供查询处理,不管返回一条记录,还是多条记录,在 Oracle 里面,一般都是通过游 标进行处理的,因此我们需要先定义一个游标类型,供我们返回记录使用的。下面定义一个 游标的包代码如下。 ------------------------------------ --功能描述:创建一个包,含有一个游标类型:(一个数据库中只需声明一次) ------------------------------------ CREATE OR REPLACE PACKAGE MyCURSOR AS TYPE cur_OUT IS REF CURSOR; End; 然后我们就可以在各个返回记录的存储过程里面使用这个游标类型了。 例如在下面的存储过程里面,返回一条指定的数据记录,那么输出参数里面需要有一个 游标的定义参数,但是我们在 C#里面使用数据访问框架来处理数据的时候,可以忽略他它 的存在,就只需要输入 p_ID 参数就可以了。 ------------------------------------ --功能描述:以字段 ID 为关键字,检索表中的数据 ------------------------------------ 文件名称:循序渐进开发 Web 项目操作说明书 第 25 页 共 65 页 Create Or Replace Procedure T_Customer_SelectByID ( cur_OUT OUT MyCURSOR.cur_OUT , p_ID IN T_CUSTOMER.ID%TYPE ) AS Begin OPEN cur_OUT FOR Select * from T_CUSTOMER Where ID = p_ID ; End; 4)提供查询处理,并返回多条记录集合;包括实体列表集合或 DataTable 集合对象 和上面返回单条记录一样,需要返回多条记录的存储过程,也需要使用一个游标的输出参数 来获取返回的记录,并可以对游标进行处理。 ------------------------------------ --功能描述:检索表中所有的数据 ------------------------------------ Create Or Replace Procedure T_Customer_SelectAll ( cur_OUT OUT MyCURSOR.cur_OUT ) AS Begin OPEN cur_OUT FOR Select * from T_CUSTOMER; End; 3.3.2. Oracle 存储过程的使用 上面小节,介绍了在 Oracle 里面,如何编写个各类存储过程,本小节主要介绍如何在 C#里面,如何对这些存储过程进行调用,并获取到对应的数据类型,如输出参数,单个数 据记录,多个数据记录等情况。 对于 Oracle 数据访问层的实现来说,它的接口实现一样简单,只是参数命名有所不同而已。 public bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null) { Hashtable inParameters = new Hashtable(); inParameters.Add("p_ID", info.ID); inParameters.Add("p_Name", info.Name); inParameters.Add("p_Age", info.Age); 文件名称:循序渐进开发 Web 项目操作说明书 第 26 页 共 65 页 return StorePorcExecute("T_Customer_Insert", inParameters, null, trans); } public bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null) { Hashtable inParameters = new Hashtable(); inParameters.Add("p_ID", info.ID); inParameters.Add("p_Name", info.Name); inParameters.Add("p_Age", info.Age); return StorePorcExecute("T_Customer_UpdateByID", inParameters, null, trans); } public List StorePorc_GetAll(DbTransaction trans = null) { return StorePorcToList("T_Customer_SelectAll", null, null, trans); } public DataTable StorePorc_GetAllToDataTable(DbTransaction trans = null) { return StorePorcToDataTable("T_Customer_SelectAll", null, null, trans); } public CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null) { Hashtable inParameters = new Hashtable(); inParameters.Add("p_ID", ID); return StorePorcToEntity("T_Customer_SelectByID", inParameters, null, trans); } public bool StorePorc_ExistByID(string ID, DbTransaction trans = null) { Hashtable inParameters = new Hashtable(); inParameters.Add("p_ID", ID); Hashtable outParameters = new Hashtable(); outParameters.Add("p_Exist", 0); StorePorcExecute("T_Customer_ExistByID", inParameters, outParameters, trans); 文件名称:循序渐进开发 Web 项目操作说明书 第 27 页 共 65 页 int exist = (int)outParameters["p_Exist"]; return exist > 0; } public bool StorePorc_DeleteByID(string ID, DbTransaction trans = null) { Hashtable inParameters = new Hashtable(); inParameters.Add("p_ID", ID); return StorePorcExecute("T_Customer_DeleteByID", inParameters, null, trans); } public int StorePorc_GetMaxAge() { Hashtable outParameters = new Hashtable(); outParameters.Add("p_MaxAge", 0); StorePorcExecute("T_Customer_MaxAge", null, outParameters, null); int MaxAge = (int)outParameters["p_MaxAge"]; return MaxAge; } 4. Web 项目的设计分析 4.1. MVC 的 Web 架构说明 我们知道,在传统基于 ASP.NET 的 Web 开发中,采用的就是 WebForm 这种方式,这 种方式可以在界面上拖动一个按钮等界面元素,然后双击可以进行后台代码进行控制相关的 内容,这些界面元素是服务端控件。这种方式虽然方便操作理解,但是可能会引入过多的业 务逻辑在后台代码里面,而随着代码量的增加,前台代码和后台代码的耦合性也很大,造成 WebForm 这种方式开发的项目,测试比较麻烦。 MVC 的 Web 开发,颠覆了过去那种服务端控件的方式,采用的是纯 HTML 控件进行 界面展示,并引入了 MVC 的模式,使得控制器、视图和模型各司其职,使得 Web 开发更 加规范、容易测试、更高效率等特点。 文件名称:循序渐进开发 Web 项目操作说明书 第 28 页 共 65 页 4.1.1. MVC 的项目目录说明 打开 Web 项目,我们可能看到下面的项目目录布局,如下所示。 界面层的目录说明如下所示 目录 说明 App_Data 放置数据的目录,如一般可以把 Sqlite 数据库文件放到这个目录下, 可以通过在配置文件添加路径进行|DataDirectory|引用。 App_Start 放置配置文件代码,如 MVC 的文件路由设置等。 Commons 放置一些界面层用到的辅助类和扩展类代码。 Content 放置 css 和除了 JavaScript 脚本,图像以外的东西 Controllers 放置 MVC 模式中的控制器类 Models 放置数据描述、操纵类和业务对象类 Scripts 放置 JavaScript 脚本 文件名称:循序渐进开发 Web 项目操作说明书 第 29 页 共 65 页 UploadFiles 附件上传的基础目录,为了方便管理,我们把附件都上传到这个基目 录下了。 Views 放置 MVC 模式中的视图类 视图目录必须和控制器的名称有对应关系,如有一个控制器为 TestController,必然有一 个视图的目录为 Test,这个 Test 目录下可能有多个 cshtml 的视图文件,一般为 Index.cshtml, 这个是主视图文件。 而 Web 框架的核心项目工程如下所示。 功能具体目录列表如下所示: 目录 说明 BLL 业务逻辑层的代码,使用代码生成的结构 文件名称:循序渐进开发 Web 项目操作说明书 第 30 页 共 65 页 Commons 公用类库里面的纯.NET 类库辅助类 ControlUtil 公用类库里面的第三方类库扩展辅助类 DAL 数据访问层的主目录 DAL/DALSQL 基于 Sqlserver 的数据访问层目录 DAL/DALSQLite 基于 SQLite 的数据访问层目录 Entity 实体层的目录 IDAL 数据访问层接口定义层目录 4.1.2. MVC 的控制器设计 MVC 的控制器很多都有相似的地方,在设计使用之初,我就希望尽可能的减少代码, 提高编程模型的统一性。因此希望能够以基类继承的方式,和我 Winform 开发框架一样, 尽可能通过基类,而不是子类的重复代码来实现各种通用的操作。 我们知道,一般我们创建一个 MVC 的控制器,都是基于 Controller 这样的基类来实现。 如下代码所示。 public class TestController : Controller { // // GET: /Test/ public ActionResult Index() 文件名称:循序渐进开发 Web 项目操作说明书 第 31 页 共 65 页 { return View(); } } 在我的 Winform 开发框架里面,用到了泛型的类型,非常方便实现业务逻辑和数据访问 基类的设计,控制器是否也可以这样做的呢? 我们知道,一般的 MVC 控制器需要验证用户是否已经登录了,这也是很多常见 Web 操 作前的验证,还有对异常的处理,在 MVC 的基类,可以一并进行记录(这个非常不错), 于是我们先来设计一个验证用户身份是否登录的基类 BaseController。 /// /// 所有需要进行登录控制的控制器基类 /// public class BaseController : Controller { /// /// 当前登录的用户属性 /// public UserInfo CurrentUserInfo { get; set; } /// /// 重新基类在 Action 执行之前的事情 /// /// 重写方法的参数 protected override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); //得到用户登录的信息 CurrentUserInfo = Session["UserInfo"] as UserInfo; //判断用户是否为空 if (CurrentUserInfo == null) { Response.Redirect("/Login/Index"); } } protected override void OnException(ExceptionContext filterContext) { base.OnException(filterContext); 文件名称:循序渐进开发 Web 项目操作说明书 第 32 页 共 65 页 //错误记录 WHC.Framework.Commons.LogTextHelper.Error(filterContext.Exception); // 当自定义显示错误 mode = On,显示友好错误页面 if (filterContext.HttpContext.IsCustomErrorEnabled) { filterContext.ExceptionHandled = true; this.View("Error").ExecuteResult(this.ControllerContext); } } ........................ } 有了这个基类,我们在主页的 Home 控制类,就可以使用用户信息对象了进行操作了, 而且必须要求客户登录了。 public class HomeController : BaseController { public ActionResult Index() { if (CurrentUserInfo != null) { ViewBag.FullName = CurrentUserInfo.FullName; ViewBag.Name = CurrentUserInfo.Name; } return View(); } ................ } 为了方便控制器对封装的业务类的调用,我们在 BaseController 的基础上再次引入一个 BusinessController控制器基类,让其默认就具有某些数据访问操作的功能,包括控制 器一般的 增 删 改 查 等 常规数据操作,这样可以把所有常规的操作都放到基类 BusinessController里面进行实现,而需要数据访问操作的控制器继承这个基类就可以 了,减少为每个控制器类都编写相似的代码,提高开发效率,增强代码的健壮性和可维护性。 /// /// 本控制器基类专门为访问数据业务对象而设的基类 /// /// 业务对象类型 /// 实体类类型 public class BusinessController : BaseController where B : class where T : WHC.Framework.ControlUtil.BaseEntity, new() 文件名称:循序渐进开发 Web 项目操作说明书 第 33 页 共 65 页 { /// /// 插入指定对象到数据库中 /// /// 指定的对象 /// 执行操作是否成功。 public virtual ActionResult Insert(T info) { bool result = false; if (info != null) { result = baseBLL.Insert(info); } return Content(result); } ................ 最终项目的控制器设计层级图如下所示。 我们以角色控制器来说明,它的定义如下所示,如果不需要实现额外的接口(除了常见 的操作),基本上不需要写任何代码了,因为所有很多常见的操作,都已经封装在了基类控 制器 BusinessController里面了。 /// /// 角色业务操作控制器 文件名称:循序渐进开发 Web 项目操作说明书 第 34 页 共 65 页 /// public class RoleController : BusinessController { public RoleController() : base() { } ............... 对于一些需要特殊数据处理的操作,可以增加一些自定义的接口函数,也可以重写基类 的一些接口,实现数据的相应处理。 4.2. Web 项目代码的生成操作 4.2.1. 核心逻辑代码生成 选择【代码生成】【Enterprise Library 代码生成】后,会提示选择数据库和表后,然后 就会根据数据库和配置信息,生成核心逻辑代码,生成的代码的结构和下面的结构相似。 文件名称:循序渐进开发 Web 项目操作说明书 第 35 页 共 65 页 4.2.2. Web 界面层代码生成 选定表并一步步进行 Web 界面生成后,最后会生成相应控制器和视图代码,控制器已经继 承了基类 BusinessController,而 Web 界面的视图已经包含常规的查询、列表、增加、编辑、 查看、删除等操作的界面和功能操作,文件如下所示。 文件名称:循序渐进开发 Web 项目操作说明书 第 36 页 共 65 页 控制器代码如下所示。 而页面视图代码则包含内容比较多,它包括了列表界面、新增、编辑、查看等层的界面定义 和数据绑定的脚本等内容。 1)相关文件和脚本的引用 上面这种方式应用 JS 文档或者 CSS 文件,是一种常见的方式,不过每个页面如果这样 应用,会显得比较臃肿,因此我在框架里面,对这个引用方式进行了优化,使用 MVC 里面 文件名称:循序渐进开发 Web 项目操作说明书 第 37 页 共 65 页 的 Bundles 属性。 在 ASP.NET MVC 出来之后,引入了一个叫做 Bundle 的东西,它用来将 js 和 css 文件 捆绑为一个块进行输出,能够极大简化界面代码,并默认对这些内容进行压缩处理,提高效 率。最终简化的界面代码如下所示。 2)通过脚本的页面内容初始化操作 文件名称:循序渐进开发 Web 项目操作说明书 第 38 页 共 65 页 以及各 DIV 层的数据绑定的等操作均在一个页面内实现。 4.3. 几种界面控件的使用 本小节介绍在 Web 框架里面,对 EasyUI 各种常规界面控件的使用,以方便我们对界面 代码的使用有进一步的了解,并为后面的界面代码分析打下基础。 4.3.1. 单行文本框 文本框,包括单行文本,多行文本,密码框等,它的使用代码都差不多。 单行文本可以使用 easyui-textbox 样式,或者 easyui-validatebox 样式,类型为 text 的控件,如下面界面所示。 界面代码如下所示: 或者 赋值给界面控件代码如下: $("#Name").val(info.Name); 获取界面控件的值代码如下: var name = $("#Name").val(); 如果是密码框,那么需要指定它为密码类型即可,其他操作和文本框一样。 4.3.2. 多行文本框 对于多行文本框,可以用下面两种方式定义它的界面。 文件名称:循序渐进开发 Web 项目操作说明书 第 39 页 共 65 页 界面代码如下所示: 或者 赋值给界面控件代码如下: $("#type_Remark").val(json.Remark); 获取界面控件的值代码如下: var text = $("#type_Remark").val(); 4.3.3. 日期输入控件 easyui 使用 class=‘easyui-datebox’来标识日期控件,从弹出的层中选择正确的日期,是 一种非常常见的界面输入控件,可以替代 My97DatePicker 日期输入控件。 文件名称:循序渐进开发 Web 项目操作说明书 第 40 页 共 65 页 弹出窗体界面效果如下。 它的界面代码如下所示: 赋值给界面控件代码如下: $("#LastUpdated").datebox('setValue', info.LastUpdated); 获取界面控件的值代码如下: var lastupate = $("#txtLastUpdated").datebox('getValue'); 4.3.4. 数值输入控件 easyui 使用样式 easyui-numberbox 标识为数值类型,其表现为文本框,但只能输入数值。 界面代码如下所示: 或者使用‘easyui-numberspinner’样式来标识,可以上下调节数值。 赋值给界面控件代码如下: 文件名称:循序渐进开发 Web 项目操作说明书 第 41 页 共 65 页 $('#nn').numberbox('setValue', 206.12); 或者 $('#ss').numberspinner('setValue', 8234725); 获取界面控件的值代码如下: var v = $('#nn').numberbox('getValue'); 或者 var v = $('#ss').numberspinner('getValue'); 4.3.5. 标签控件 标签控件一般用于展示查看信息,不允许用户修改内容的界面。下面是一个对于名称标签使 用的代码。
还剩64页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

xiaodjy

贡献于2016-04-19

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf