三层架构是企业信息管理系统中一种比较流行的架构方式,如大家所知,三层架构将信息系统分为数据访问层(DAL)、业务逻辑层(BLL)、界面表示层(UI)三部分,三层架构的好处是根据系统中代码所处的层次将系统拆开,而通过业务模型(Model)再进行连接,降低系统各层次之间的耦合度,提升程序开发和后期维护的容易度。

由于三层架构是根据由上至下的层次进行分层,而不是根据功能、应用领域进行分层,所以三层架构在每一层的关注点并不相同,数据访问层关注的是跟数据库打交道的部分,业务逻辑层关注的是业务逻辑处理部分,而界面表示层关注的是人机交互部分,所以三层架构在一定程度上也体现出了系统开发的先后顺序和分工。

本文将从我对三层架构的理解上,利用General框架从头开始打造一个信息管理系统的初步结构,以此来展示General框架在信息管理系统开发上的优势。由于不同的人对架构的理解也不一样,所以本文不强调架构的正确性,只是出于简化开发、方便编程的原则下提供一个三层架构的样本。本文中的示例工程为一个小型的商品库存管理软件,源代码请查看General框架中的Sample.Market工程。

第一步、设计业务模型

由于是示例工程,所以本文绕过需求分析过程,直接从业务模型设计开始。关于为何从业务模型开始,其实目前主要有两种开发顺序,即业务驱动设计和界面驱动设计,业务驱动设计更强调业务作为系统的核心,所有编程工作都围绕业务设计展开,而界面驱动设计认为界面是客户、项目经理、设计师、程序员最好的沟通工具,所以一切以界面为首要确定目标。而我认为这两种方式至于哪种更好,需要看实际情况,假如项目不是很大,而界面又容易确定或是客户比较注重界面设计,那界面驱动设计当然为优选方案,可以先从界面入手,做出DEMO,等客户满意后再做业务流程的开发。反之,如果业务逻辑更为重要,当然是以业务驱动设计为优选方案,而信息管理系统的开发,大部分还是以业务为中心,所以除去需求分析之后的第一步也就以业务模型设计为先。

业务模型设计可以借助Excel、PowerDesinger等工具,先用Excel整理好业务模型比较核心的数据信息,然后通过PowerDesinger做出业务模型图(即ER图),再生成物理数据库模型,再生成数据库。本文的业务模型图设计如下,并生成了对应各种数据库的物理数据库模型,再生成了数据库的建库脚本,Acess通过脚本创建数据库需要一定的小技巧,这个方法可以在百度中查到。

我通过PowerDesinger生成的建库脚本,分别创建了Access、Sqlite、SqlServer2000、SqlServer2005、Oracle、MySql的数据库,数据库名称都为Market。

第二步、生成实体模型

数据库创建完成后,先用VisualStudio创建名为Sample.Market的解决方案,并创建Sample.Market.Logic(业务逻辑层)、Sample.Market.Model(实体模型库)、Sample.Market.WinForm(WinForm界面表示层),大家发现为什么没有创建数据访问层呢,因为我是利用General框架进行开发,而General框架支持多数据库并且有ORM功能,所以数据访问层就显得不是必须的了,也可以将General.Data理解为通用的数据访问层。但是从系统解耦和更针对性的多数据库支持出发,再增加一个系统内的数据访问层也有好处,但会带来更多的编码和更多的后期维护成本,其中利弊需要自己权衡。

工程创建完毕后,利用General代码生成器这个利器,我们就可以很快的一次性把实体模型生成出来,注意这个实体模型是从数据库生成而来的,而实际上实体模型应从业务模型而来,因为数据库是业务模型生成而来,而实体模型又是从数据库生成而来,所以这三者就成了完全一致的,这样在开发角度其实更方便实用,因为只要了解其中一者就可以对三者完全了解。但问题是如果业务模型发生变化怎么办,这个问题也困扰我很久,因为虽然有先进的工具做支持,而从业务模型生成物理数据模型,再修改数据库结构,再从数据库生成实体类,依然是非常累人的一件事,直到目前我也没有特别好的办法解决这个问题,甚至曾想过制作一个从设计业务模型到生成数据库再到生成实体类的完整解决方案工具,但奈何工作量太大是我难以完成的,在此只能提出以下几点供大家参考:

1)尽量减少业务模型的修改,前期设计要尽量完善,留足冗余字段,并告知负责业务调研同事修改的成本,修改尽量要求客户签字确认;

2)将数据库和测试数据分开,比如通过SQL脚本录入测试数据,免得修改数据库结构造成测试数据丢失;

3)工具不是人,没有人的智能,如果对数据库结构或实体类的某个地方修改了,而重新建库或是重新生成实体类后又忘记复原,容易导致莫名其妙的BUG,所以尽量避免重新建库或一次重新生成所有的实体类,而是小范围修改;

4)如果实在改动太多,就抛弃业务模型,直接去数据库修改吧,这样减少了一大部分工作量,可以避免积劳成疾。

General代码生成器的使用可以参考压缩包内的说明教程,生成实体的模板已经包含在模板库中,动手能力强的可以自己修改或制作符合自己习惯的模板。

第三步、创建界面表示层

制作软件界面其实是一项非常累人的工作,大概占了整个系统开发的一半强的时间,但是在界面开发上也有很多窍门,比如通过配置动态生成菜单、通过配置动态生成表格列、表单自动生成自动收集填充、下拉框自动生成、搜索项自动生成并自动生成查询语句、呈现器自动绑定字典等等,可以说是只有想不到没有做不到,通过这些技巧可以大大提高开发的效率,减少很多重复工作,在以后我会慢慢介绍这些技巧,本文中将介绍一个表单自动收集填充的技巧。

在General.WinForm.FormHelper和General.Web.WebHelper中都有一个名为CollectAndFill的方法,分别对应窗体和网页的收集和填充,可以对 Object、DataRow、控件集这三者中的任意两者之间进行收集和填充工作,比如对表单控件收集值并赋值到实体,对实体收集值并赋值给表单,但前提是需要将表单的控件命名为与实体的属性相同的名称以进行对应。使用方法如:

创建实体并收集表单值:

 Goods good = new Goods();
 FormHelper.CollectAndFill(this, good);

查询实体并赋值给表单

 Goods good = goodsLogic.GetGoodByCode(编号.Text);
 FormHelper.CollectAndFill(good, this);

其实控件的收集填充工作是通过各种控件对应的工具类来完成的,所以如果有收集或填充不到的控件类型,可以通过增加并注册新的工具类来实现,具体请参考源代码。

第四步、创建业务逻辑层

最重要的业务逻辑却放在最后,因为当业务模型和界面都确定之后,业务逻辑其实也跑不出圈了,只要根据需要对应完成就行了,可能有人说是不是应当先创建业务逻辑再制作界面,其实这样业务逻辑的方法反而难以确定,还是以界面制作为优先。

General代码生成器也可以生成业务逻辑层的代码,甚至说界面层的代码也完全可以生成,如果项目代码优化的比较好,可以通过先生成再修改的办法来节约很多时间,但大部分情况这个工作重复度并不高,所以不需要自动生成来完成。

创建业务逻辑类后,通过DataManager来进行数据库访问操作,比如下面这个通过ID获取商品信息的方法:

         /// <summary>
         /// 通过ID获取商品信息
         /// </summary>
         /// <param name="id">商品ID</param>
         /// <returns></returns>
         public Goods GetGoodByID(int id)
         {
             return DataManager.Default.Find<Goods>(id);
         }

通过一行代码就可以完成;

再如商品入库的方法:

 1         /// <summary>
 2         /// 商品入库
 3         /// </summary>
 4         /// <param name="good">商品信息</param>
 5         /// <param name="stockDate">入库日期</param>
 6         /// <param name="amount">入库数量</param>
 7         /// <returns></returns>
 8         public ReturnCode StockInGoods(Goods good, DateTime stockDate, int amount)
 9         {
             Transaction tran = DataManager.Default.BeginTransaction();
             try
             {
                 // 查询是否有编号已存在的商品
                 Goods tmpGood = DataManager.Default.FindFirst<Goods>("编号 = @bh", good.编号);
                 if (tmpGood != null)
                 {
                     good.ID = tmpGood.ID;
                     good.Attach();
                     good.库存数量 = tmpGood.库存数量;
                     good.库存金额 = tmpGood.库存金额;
                     good.进货数量 = tmpGood.进货数量;
                     good.进货金额 = tmpGood.进货金额;
                 }
                 else
                 {
                     good.Detach();
                 }
 
                 if (good.库存数量 == null) good.库存数量 = ;
                 if (good.库存金额 == null) good.库存金额 = ;
                 if (good.进价 == null) good.进价 = ;
                 if (good.进货数量 == null) good.进货数量 = ;
                 if (good.进货金额 == null) good.进货金额 = ;
 
                 // 调整库存
                 good.库存数量 += amount;
                 good.库存金额 += amount * good.进价;
                 good.进货数量 += amount;
                 good.进货金额 += amount * good.进价;
                 good.平均进价 = good.进货金额 / good.进货数量;
                 DataManager.Default.Save(good);
 
                 // 保存入库记录
                 GoodsIn goodsIn = new GoodsIn();
                 goodsIn.Goo_ID = good.ID;
                 goodsIn.日期 = stockDate;
                 goodsIn.编号 = good.编号;
                 goodsIn.进价 = good.进价;
                 goodsIn.零售价 = good.零售价;
                 goodsIn.数量 = amount;
                 goodsIn.金额 = amount * goodsIn.进价;
                 goodsIn.生产日期 = good.生产日期;
                 goodsIn.保质期 = good.保质期;
                 goodsIn.到期日期 = good.到期日期;
                 goodsIn.生产批号 = good.生产批号;
                 goodsIn.供应商 = good.供应商;
                 DataManager.Default.Save(goodsIn);
 
                 tran.Commit();
                 return ReturnCode.Successed;
             }
             catch
             {
                 tran.Rollback();
                 throw;
             }
         }        

即便是对两个表进行操作并且使用事务,代码依旧非常简短。

由于各家数据库的SQL都稍有差别,所以要做到兼容多种数据库其实是非常困难的,General框架的数据库操作是通过拼接SQL语句来完成的,没有像EntityFramework的Linq或NHibernate的HQL这样的自身查询语言,所以要兼容多种数据库是难以实现的,而这个示例程序能够兼容Access、Sqlite、SqlServer、Oracle、MySql多种数据库,主要是因为以下几点:

1)General框架对各种数据库有对应的查询生成器,同一个方法对不同数据库有不同的SQL生成实现;

2)General框架具有不同参数前缀替换能力,比如在Oracle中参数前缀是“:”而不是SqlServer的“@”,General框架会自动将作为通用前缀的“@”替换为Oracle的“:”以便兼容;

3)本示例中没有使用一些特殊的SQL语句,各家的数据库在常用的select、insert、update、delete语句上兼容的比较好。

最后,大家可以打开General框架中的示例程序查看具体的使用方法,里面还包含一个性能测试程序可以测试General框架的性能,相关的源码下载请参看上一篇文章,欢迎大家评论。

利用General框架进行三层架构开发的更多相关文章

  1. 用VS2010创建三层架构开发模式及三层架构的研究

    三层架构的研究 三层体系结构的概念 用户界面表示层(USL) 业务逻辑层(BLL) 数据访问层(DAL) BLL将USL与DAL隔开了,并且加入了业务规则   各层的作用 1:数据数据访问层:主要是对 ...

  2. DONET三层架构开发初步

    .NET三层架构开发初步 今天咱们来谈下三层架构.说到三层架构,搞过点程序的可能都知道三层架构的概念.但是对三层的精髓可能不是很了解. 首先说下自己对三层的理解,就是使用三个(多个)项目结合起来开发出 ...

  3. 利用General框架开发RDLC报表

    RDLC是微软推出的自家的报表软件,虽然没有一些第三方的报表软件强大好用,但是作为VisualStudio集成的报表工具,在客户要求不高的情况下还是非常值得一用的,本文将介绍通过General代码生成 ...

  4. 利用Dapper ORM搭建三层架构

    利用Dapper关系对象映射器写的简单的三层架构.Dapper:StackOverFlow在使用的一个微型的ORM,框架整体效率较高,轻量级的ORM框架.网上有较多的扩展.此处只是简单的调用Dappe ...

  5. MVC框架与三层架构

    MVC框架 介绍: MVC全名Model View Controller Model:模型的意思,代表业务模型 View:视图的意思,代表用户界面 Controller:控制器的意思,控制器接受用户的 ...

  6. .Net 三层架构开发初步

    写在前面的话:在课堂上只是听老师讲过三层架构,知道大概是什么意思,我的理解就是将本来混合着写在一起的代码按功能性的不同分别写在不同的项目中,然后上层项目调用下层项目提供的接口,这样可以使代码的层次更清 ...

  7. .NET三层架构开发初步

    转自:https://www.cnblogs.com/weilengdeyu/archive/2013/01/04/2844847.html 今天咱们来谈下三层架构.说到三层架构,搞过点程序的可能都知 ...

  8. spring框架中三层架构相关的注解

    做了这么多年的C++,再去学Java,确实发现,语言都是相通的,即使是Java的那么多生态,理解起来也并不费劲 Spring 框架目前还在学习中,处于 Tourist 阶段,目前只求会做,不求原理,等 ...

  9. ssh三大框架,三层架构 整合测试!完整分页代码,JdbcTemplate等测试,存储过程调用,留着以后复习吧

    下载地址:http://download.csdn.net/detail/liangrui1988/5760453

随机推荐

  1. 30 python 并发编程之多线程

    一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍 官网链接:https://docs.python ...

  2. JVM_总结_02_Java技术体系

    一.前言 此系列随笔是针对<深入理解Java虚拟机:JVM高级特性与最佳实践 第2版>的总结 上一节,我们搭建好了java的开发环境,这一节,我们来看Java的技术体系 二.Java技术体 ...

  3. rabbitmq_学习_01_rabbitmq安装

    二.参考资料 1.RabbitMQ:windows10下安装 2.RabbitMQ系列(一):Windows下RabbitMQ安装及入门

  4. web 压力测试工具ab压力测试详解

    Web性能压力测试工具之ApacheBench(ab)详解 原文:http://www.ha97.com/4617.html PS:网站性能压力测试是性能调优过程中必不可少的一环.只有让服务器处在高压 ...

  5. java-05String课堂练习

    1.阅读以下代码查看输出结果 public class StringPool { public static void main(String args[]) { String s0="He ...

  6. uoj#87. mx的仙人掌

    //Achen #include<bits/stdc++.h> #define For(i,a,b) for(int i=(a);i<=(b);i++) #define Rep(i, ...

  7. 理解SQL查询的底层原理

    阅读目录 一.SQL Server组成部分 二.查询的底层原理 本系列[T-SQL]主要是针对T-SQL的总结. T-SQL基础 [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础] ...

  8. Linux终端录屏与播放 script 命令

    本文由Suzzz原创,发布于 http://www.cnblogs.com/Suzzz/p/4107700.html ,转载请保留此声明. 有时候可能想要记录在终端的所有操作包括输出等,将来作为视频播 ...

  9. selenium+headless chrome安装使用

    pip install selenium 因为phantomJS将停止维护,所以建议使用headless chromeChromeDriver is a separate executable tha ...

  10. 一般处理程序+html 的CRUD

    using Console_Core.BLL; using Console_Core.Common; using Console_Core.Model; using System; using Sys ...