摘自:http://www.tzwhx.com/newOperate/html/1/11/113/13404.html 比较不错的文章
基于.NET平台的分层架构实战(一)——综述
通过浏览博客园的文章发现,很多朋友对分层架构特别感兴趣,刚好我刚做完的毕业设计就是专门研究.NET平台上分层架构的(题目叫“基于.NET平台的分层架构与设计模式应用研究”)。通过做这篇论文,我对分层架构有了一定的了解,所以,就萌发了想写一个文章系列,详述一下分层架构。然而,论文的理论性太强,不适合在网上发布,尤其不适合初学者理解,所以,我想在这个文章系列中,少讲理论,而是通过做一个完整的案例来讨论分层架构的基本方法,这样会直观很多。希望在这个文章系列的写作过程中,能和朋友们一起学习,一起进步。
为了让朋友们把主要精力放在理解分层架构而不是案例本身,我准备选择一个相对简单的留言本系统作为Demo,这个系统的名字就叫做NGuestBook。
初步计划将这个文章系列分为以下几篇:
1.综述
2.系统需求分析及数据库设计
3.架构概要设计
4.实体类的实现
5.接口的设计与实现
6.依赖注入及IoC的设计与实现
7.数据访问层的第一种实现——Access+动态生成SQL语言
8.数据访问层的第二种实现——SQLServer+存储过程
9.数据访问层的第三种实现——基于NBear框架的ORM实现
10.业务逻辑层的实现
11.表示层的实现
12.使用ASP.NET AJAX框架对表示层进行改进
13.总结
当然,以上只是初步计划,在写文章的过程中可能会根据具体情况适当调整,但是内容大体就是这些。
这个文章系列不会对所用到的技术进行详细讲解,具体请参考相关文献,阅读文章前最好能对以下技术有一个了解:
1.C#语言
2.ASP.NET
3.设计模式
4.关系数据库基础知识
5.软件架构基本原则与软件工程基础知识
6.基于NBear框架的ORM技术
7.JavaScript,Ajax
8.ASP.NET AJAX框架(特别是客户端编程)
9.HTML,CSS,标准化布局
另外,本文章系列是基于.NET framework2.0框架平台进行讨论,3.5平台的新特性(如LINQ、ASP.NET MVC等)不会讨论,IDE使用Visual Studio 2005,数据库会用到SQLServer2005 Express和Access2003。
基于.NET平台的分层架构实战(二)——需求分析与数据库设计
在实际的项目中,需求分析和数据库的设计是很重要的一个环节,这个环节会直接影响项目的开发过程和质量。实际中,这个环节不但需要系统分析师、软件工程师等计算机方面的专家,还需要相关领域的领域专家参与才能完成。
但是,在这个文章系列中,所要使用的Demo仅仅是一个例子,而且其业务极为简单,因此,这里并不是真正的需求分析和数据库设计,而是将Demo的需求和数据库罗列至此,使朋友们对Demo有一个大体的了解,方便后续文章中开发过程的理解。
需求分析:
这个项目是一个留言本,其业务极为简单,现将其描述如下。
1.任何访问者可以进行留言,留言完成后,不会立即显示正文,而是要经过管理员验证后才可显示。
2.任何访问者可以对留言发表评论,未通过验证的留言不可以评论。
3.管理员可以对留言进行回复(这个回复不同于评论,是直接显示在正文下面,而且是一个留言只能有一个回复),并可对留言与评论实行删除,以及对留言进行通过验证操作。
4.管理员分为超级管理员和普通管理员。超级管理员只有一个,负责对普通管理员实行添加、删除操作。普通管理员可偶多个,负责对留言的管理,并可以修改自己的登录密码。
这个项目的用例图如下:
设计数据表之前,首先进行实体和关系的识别与确定。
通过需求分析,可以观察得出,本项目的实体有:管理员(不包括超级管理员),留言,评论。本项目的关系有:留言与评论间的一对多关系。
进一步,数据库各表的设计如下:
管理员表(TAdmin)
ID int 管理员ID NotNull 主键,自增
Name varchar(20) 登录名 NotNull
Password varchar(50) 登录密码 NotNull 使用MD5加密
留言表(TMessage)
ID int 留言ID NotNull 主键,自增
GuestName varchar(20) 留言者用户名 NotNull
GuestEmail varchar(100) 留言者E-mail Null
Content text 留言内容 NotNull
Time datetime 发表留言时间 NotNull
Reply text 回复 Null
评论表(T Comment)
Content text 评论内容 NotNull
Time datetime 发表评论时间 NotNull
MessageID int 所属留言的ID 外键
架构基本原则:
这里,将描述一些在这个架构设计中的基本原则,其中很多都是经典的设计原则,不过针对分层架构的特点,用我自己的语言进行了描述。其中也有我自己提出的原则。
逐层调用原则及单向调用原则
现在约定将N层架构的各层依次编号为1、2、…、K、…、N-1、N,其中层的编号越大,则越处在上层。那么,我们设计的架构应该满足以下两个原则:
1.第K(1 2.如果P层依赖Q层,则P的编号一定大于Q。
其中第一个原则,保证了依赖的逐层性,及整个架构的依赖是逐层向下的,而不能跨层依赖。第二个原则,则保证了依赖的单向性,及只能上层依赖底层,而不能底层反过来依赖上层。
针对接口编程,而不是针对实现编程
这里所指的接口,不是特指编程语言中的具体语言元素(如C#中由Interface定义的语言接口),而是指一种抽象的,在语义层面上起着接合作用语义体。它的具体实现,可能是接口,可能是抽象类,甚至可能是具体类。
我认为,从不同的视角,接口可以有以下两种定义:
1.接口是一组规则的集合,它规定了实现本接口的类或接口必须拥有的一组规则。体现了自然界“如果你是……则必须能……”的理念。
2.接口是在一定粒度视图上同类事物的抽象表示。注意这里我强调了在一定粒度视图上,因为“同类事物”这个概念是相对的,它因为粒度视图不同而不同。
具体到N层架构中,针对接口编程的意义在部分上是这样的:
现仍约定将N层架构的各层依次编号为1、2、…、K、…、N-1、N,其中层的编号越大,则越处在上层,那么第K层不应该依赖具体一个K-1层,而应该依赖一个K-1层的接口,即在第K层中不应该有K-1层中的某个具体类。
依赖倒置原则
在软件设计原则中,有一种重要的思想叫做依赖倒置。它的核心思想是:不能让高层组件依赖底层组件,而且,不管高层组件和底层组件,两者都应依赖于抽象。
那么,这个原则和我们上面的原则是否矛盾呢?其实并不矛盾。
因为这个原则定义中的“依赖”是指“具体依赖”,而上面定义中的依赖全部指“抽象依赖”。我对这两种依赖的定义如下:
具体依赖——如果P层中有一个或一个以上的地方实例化了Q层中某个具体类,则说P层具体依赖于Q层。
抽象依赖——如果P层没有实例化Q层中的具体类,而是在一个或一个以上的地方实例化了Q层中某个接口,则说P层抽象依赖于Q层,也叫接口依赖于Q层。
从这两个定义可以看到,所谓的依赖倒置原则,正是上面提到针对接口编程,而不是针对实现编程,两者在本质上是统一的。
综上所述,可以看出,本课题设计的分层架构,应该是这样一种架构:
1.N层架构的各层依次编号为1、2、…、K、…、N-1、N,其中层的编号越大,则越处在上层。
2.架构中仅存在一种依赖,即第K层接口依赖第K-1层,其中1
封装变化原则
封装变化的原则定义为:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混杂在一起。
开放-关闭原则
开发-关闭原则定义为:对扩展开放,对修改关闭。
具体到N层架构中,可以描述为:当某一层有了一个新的具体实现时,它应该可以在不修改其他层的情况下,与此新实现无缝连接,顺利交互。
单一归属原则
在这个架构中,任何一个操作类都应该有单一的职责,属于单独的一层,而不能同时担负两种职责或属于多个层次(实体类及辅助类可以被多个层使用,但它们不属于任何一个层,而是独立存在)。
层次划分:
目前,典型的分层架构是三层架构,即自底向上依次是数据访问层、业务逻辑层和表示层。
这种经典架构经历了时间的考验和实践的多次检验,被认为是合理、有效的分层设计,所以,在本文中,将沿袭这种经典架构,使用数据访问层、业务逻辑层和表示层的三层架构体系。
职责划分:
目前,在典型的三层架构中,对层次各自的职责划分并没有一个统一的规范,综合现有的成功实践和.NET平台的特殊性,在本文中将三层架构的职责划分如下:
数据访问层——负责与数据源的交互,即数据的插入、删除、修改以及从数据库中读出数据等操作。对数据的正确性和有效性不负责,对数据的用途不了解,不负担任何业务逻辑。
业务逻辑层——负责系统领域业务的处理,负责逻辑性数据的生成、处理及转换。对流入的逻辑性数据的正确性及有效性负责,对流出的逻辑性数据及用户性数据不负责,对数据的呈现样式不负责。
表示层——负责接收用户的输入、将输出呈现给用户以及访问安全性验证。对流入的数据的正确性和有效性负责,对呈现样式负责,对流出的数据正确性不负责,但负责在数据不正确时给出相应的异常信息。
模块划分及交互设计:
综合以上分析,可在宏观上将整个系统分为一下几个模块:
实体类模块——一组实体类的集合,负责整个系统中数据的封装及传递。
数据访问层接口族——一组接口的集合,表示数据访问层的接口。
业务逻辑层接口族——一组接口的集合,表示业务逻辑层的接口。
数据访问层模块——一组类的集合,完成数据访问层的具体功能,实现数据访问层接口族。
业务逻辑层模块——一组类的集合,完成业务逻辑层的具体功能,实现业务逻辑层接口族。
表示层模块——程序及可视元素的集合,负责完成表示层的具体功能。
IoC容器模块——负责依赖注入的实现。
辅助类模块——完成全局辅助性功能。
各模块见交互关系如下:
这篇中理论比较多,但是它是整个架构的基础,可以帮助朋友们对将要实现的项目架构及要遵循的原则有一个整体的了解。当然,在后续文章中,将主要讨论Demo项目的实际开发过程,那时,这些思想和理论性的东西将得到体现。
基于.NET平台的分层架构实战(四)——实体类的设计与实现
实体类是现实实体在计算机中的表示。它贯穿于整个架构,负担着在各层次及模块间传递数据的职责。一般来说,实体类可以分为“贫血实体类”和“充血实体类”,前者仅仅保存实体的属性,而后者还包含一些实体间的关系与逻辑。我们在这个Demo中用的实体类将是“贫血实体类”。
大多情况下,实体类和数据库中的表(这里指实体表,不包括表示多对多对应的关系表)是一一对应的,但这并不是一个限制,在复杂的数据库设计中,有可能出现一个实体类对应多个表,或者交叉对应的情况。在本文的Demo中,实体类和表是一一对应的,并且实体类中的属性和表中的字段也是对应的。
在看实体类的代码前,先看一下系统的工程结构。
如上图所示,在初始阶段,整个系统包括6个工程,它们的职责是这样的:
Web——表示层
Entity——存放实体类
Factory——存放和依赖注入及IoC相关的类
IBLL——存放业务逻辑层接口族
IDAL——存放数据访问层接口族
Utility——存放各种工具类及辅助类
这只是一个初期架构,主要是将整个系统搭一个框架,在后续开发中,将会有其他工程被陆陆续续添加进来。
我们的实体类将放在Entity工程下,这里包括三个文件:AdminInfo.cs,MessageInfo.cs,CommentInfo.cs,分别是管理员实体类、留言实体类和评论实体类。具体代码如下:
AdminInfo.cs:
1using System;
2
3namespace NGuestBook.Entity
4{
5 /**////
6 /// 实体类-管理员
7 ///
8 [Serializable]
9 public class AdminInfo
10 {
11 private int id;
12 private string name;
13 private string password;
14
15 public int ID
16 {
17 get { return this.id; }
18 set { this.id = value; }
19 }
20
21 public string Name
22 {
23 get { return this.name; }
24 set { this.name = value; }
25 }
26
27 public string Password
28 {
29 get { return this.password; }
30 set { this.password = value; }
31 }
32 }
33}
34
MessageInfo.cs:
1using System;
2
3namespace NGuestBook.Entity
4{
5 /**////
6 /// 实体类-留言
7 ///
8 [Serializable]
9 public class MessageInfo
10 {
11 private int id;
12 private string guestName;
13 private string guestEmail;
14 private string content;
15 private DateTime time;
16 private string reply;
17 private string isPass;
18
19 public int ID
20 {
21 get { return this.id; }
22 set { this.id = value; }
23 }
24
25 public string GuestName
26 {
27 get { return this.guestName; }
28 set { this.guestName = value; }
29 }
30
31 public string GuestEmail
32 {
33 get { return this.guestEmail; }
34 set { this.guestEmail = value; }
35 }
36
37 public string Content
38 {
39 get { return this.content; }
40 set { this.content = value; }
41 }
42
43 public DateTime Time
44 {
45 get { return this.time; }
46 set { this.time = value; }
47 }
48
49 public string Reply
50 {
51 get { return this.reply; }
52 set { this.reply = value; }
53 }
54
55 public string IsPass
56 {
57 get { return this.isPass; }
58 set { this.isPass = value; }
59 }
60 }
61}
62
CommentInfo.cs:
1using System;
2
3namespace NGuestBook.Entity
4{
5 /**////
6 /// 实体类-评论
7 ///
8 [Serializable]
9 public class CommentInfo
10 {
11 private int id;
12 private string content;
13 private DateTime time;
14 private int message;
15
16 public int ID
17 {
18 get { return this.id; }
19 set { this.id = value; }
20 }
21
22 public string Content
23 {
24 get { return this.content; }
25 set { this.content = value; }
26 }
27
28 public DateTime Time
29 {
30 get { return this.time; }
31 set { this.time = value; }
32 }
33
34 public int Message
35 {
36 get { return this.message; }
37 set { this.message = value; }
38 }
39 }
40}
41
大家可以看出,实体类的代码很简单,仅仅是负责实体的表示和数据的传递,不包含任何逻辑性内容。下篇将介绍接口的设计
接下来,将进行接口的设计。这里包括数据访问层接口和业务逻辑层接口。在分层架构中,接口扮演着非常重要的角色,它不但直接决定了各层中的各个操作类需要实现何种操作,而且它明确了各个层次的职责。接口也是系统实现依赖注入机制不可缺少的部分。
本项目的接口设计将按如下顺序进行:
1.首先由前文的需求分析,列出主要的UI部分。
2.分析各个UI需要什么业务逻辑支持,从而确定业务逻辑层接口。
3.分析业务逻辑层接口需要何种数据访问操作,从而确定数据访问层接口。
另外,为保证完全的面向对象特性,接口之间的数据传递主要靠实体类或实体类集合,禁止使用DataTable等对象传递数据。
由需求分析,列出主要UI
需求分析部分,请参看基于.NET平台的分层架构实战(二)——需求分析与数据库设计 。有需求分析,可以列出系统中主要应包括以下UI:
UI01——主页面,列出全部的留言及相应评论,支持分页显示。留言按发表时间逆序显示,评论紧跟在相应留言下。管理员可以通过相应链接对留言执行通过验证、删除、回复以及对评论进行删除操作。游客可通过相应连接进入发表留言评论页面。
UI02——发表留言页面,供游客发表新留言。
UI03——发表评论页面,供游客发表评论。
UI04——回复留言页面,供管理员回复留言。
UI05——管理员登录页面。
UI06——管理员修改个人密码的页面。
UI07——超级管理员登录后的页面,主要提供管理员列表。可以通过相应链接将指定管理员删除。
UI08——添加新管理员的页面。
UI09——操作成功完成后的跳转提示页面。
UI10——系统出现异常时显示友好出错信息的页面。
由UI识别业务逻辑操作
UI01:按分页取得留言,按指定留言取得全部评论,将指定留言通过验证,将指定留言删除,将指定评论删除
UI02:添加新留言
UI03:添加新评论
UI04:回复留言
UI05:管理员登录
UI06:修改管理员密码
UI07:取得全部管理员信息,删除管理员
UI08:添加新管理员
经过整理,可得以下接口操作:
IAdminBLL:Add(添加管理员),Remove(删除管理员),ChangePassword(修改管理员密码),Login(管理员登录),GetAll(取得全部管理员)
IMessageBLL:Add(添加留言),Remove(删除留言),Revert(回复留言),Pass(将留言通过验证),GetByPage(按分页取得留言)
ICommentBLL:Add(添加评论),Remove(删除评论),GetByMessage(按留言取得全部评论)
这三个接口文件都放在IBLL工程下,具体代码如下:
IAdminBLL.cs:
1using System;
2using System.Collections.Generic;
3using System.Text;
4using NGuestBook.Entity;
5
6namespace NGuestBook.IBLL
7{
8 /**////
9 /// 业务逻辑层接口-管理员
10 ///
11 public interface IAdminBLL
12 {
13 /**////
14 /// 添加管理员
15 ///
16 /// 新管理员实体类
17 /// 是否成功
18 bool Add(AdminInfo admin);
19
20 /**////
21 /// 删除管理员
22 ///
23 /// 欲删除的管理员的ID
24 /// 是否成功
25 bool Remove(int id);
26
27 /**////
28 /// 修改管理员密码
29 ///
30 /// 欲修改密码的管理员的ID
31 /// 新密码
32 /// 是否成功
33 bool ChangePassword(int id,string password);
34
35 /**////
36 /// 管理员登录
37 ///
38 /// 管理员登录名
39 /// 管理员密码
40 /// 如果登录成功,则返回相应管理员的实体类,否则返回null
41 AdminInfo Login(string name,string password);
42
43 /**////
44 /// 取得全部管理员信息
45 ///
46 /// 管理员实体类集合
47 IList<AdminInfo> GetAll();
48 }
49}
IMessageBLL.cs:
1using System;
2using System.Collections.Generic;
3using System.Text;
4using NGuestBook.Entity;
5
6namespace NGuestBook.IBLL
7{
8 /**////
9 /// 业务逻辑层接口-留言
10 ///
11 public interface IMessageBLL
12 {
13 /**////
14 /// 添加留言
15 ///
16 /// 新留言实体类
17 /// 是否成功
18 bool Add(MessageInfo message);
19
20 /**////
21 /// 删除留言
22 ///
23 /// 欲删除的留言的ID
24 /// 是否成功
25 bool Remove(int id);
26
27 /**////
28 /// 回复留言
29 ///
30 /// 要回复的留言的ID
31 /// 回复信息
32 /// 是否成功
33 bool Revert(int id, string reply);
34
35 /**////
36 /// 将留言通过验证
37 ///
38 /// 通过验证的留言的ID
39 /// 是否成功
40 bool Pass(int id);
41
42 /**////
43 /// 按分页取得留言信息
44 ///
45 /// 每页显示几条留言
46 /// 当前页码
47 /// 留言实体类集合
48 IList<MessageInfo> GetByPage(int pageSize,int pageNumber);
49 }
50}
ICommentBLL.cs
1using System;
2using System.Collections.Generic;
3using System.Text;
4using NGuestBook.Entity;
5
6namespace NGuestBook.IBLL
7{
8 /**////
9 /// 业务逻辑层接口-评论
10 ///
11 public interface ICommentBLL
12 {
13 /**////
14 /// 添加评论
15 ///
16 /// 新评论实体类
17 /// 是否成功
18 bool Add(CommentInfo comment);
19
20 /**////
21 /// 删除评论
22 ///
23 /// 欲删除的评论的ID
24 /// 是否成功
25 bool Remove(int id);
26
27 /**////
28 /// 取得指定留言的全部评论
29 ///
30 /// 指定留言的ID
31 /// 评论实体类集合
32 IList<CommentInfo> GetByMessage(int messageId);
33 }
34}
由业务逻辑确定数据访问操作
IAdminBLL需要的数据访问操作:插入管理员,删除管理员,更新管理员信息,按ID取得管理员信息,按登录名与密码取得管理员,取得全部管理员
IMessageBLL需要的数据访问操作:插入留言,删除留言,更新留言信息,按ID取得留言信息,按分页取得留言
ICommentBLL需要的数据访问操作:插入评论,删除评论,按留言取得全部评论
另外,添加管理员时需要验证是否存在同名管理员,所以需要添加一个“按登录名取得管理员”。
对以上操作进行整理,的如下接口操作:
IAdminDAL:Insert,Delete,Update,GetByID,GetByNameAndPassword,GetAll
IMessageDAL:Insert,Delete,Update,GetByID,GetByPage
ICommentDAL:Insert,Delete,GetByMessage
这三个接口文件放在IDAL工程下,具体代码如下:
IAdminDAL.cs:
1using System;
2using System.Collections.Generic;
3using System.Text;
4using NGuestBook.Entity;
5
6namespace NGuestBook.IDAL
7{
8 /**////
9 /// 数据访问层接口-管理员
10 ///
11 public interface IAdminDAL
12 {
13 /**////
14 /// 插入管理员
15 ///
16 /// 管理员实体类
17 /// 是否成功
18 bool Insert(AdminInfo admin);
19
20 /**////
21 /// 删除管理员
22 ///
23 /// 欲删除的管理员的ID
24 /// 是否成功
25 bool Delete(int id);
26
27 /**////
28 /// 更新管理员信息
29 ///
30 /// 管理员实体类
31 /// 是否成功
32 bool Update(AdminInfo admin);
33
34 /**////
35 /// 按ID取得管理员信息
36 ///
37 /// 管理员ID
38 /// 管理员实体类
39 AdminInfo GetByID(int id);
40
41 /**////
42 /// 按管理员名取得管理员信息
43 ///
44 /// 管理员名
45 /// 管理员实体类
46 AdminInfo GetByName(string name);
47
48 /**////
49 /// 按用户名及密码取得管理员信息
50 ///
51 /// 用户名
52 /// 密码
53 /// 管理员实体类,不存在时返回null
54 AdminInfo GetByNameAndPassword(string name,string password);
55
56 /**////
57 /// 取得全部管理员信息
58 ///
59 /// 管理员实体类集合
60 IList<AdminInfo> GetAll();
61 }
62}
IMessageDAL.cs:
1using System;
2using System.Collections.Generic;
3using System.Text;
4using NGuestBook.Entity;
5
6namespace NGuestBook.IDAL
7{
8 /**////
9 /// 数据访问层接口-留言
10 ///
11 public interface IMessageDAL
12 {
13 /**////
14 /// 插入留言
15 ///
16 /// 留言实体类
17 /// 是否成功
18 bool Insert(MessageInfo message);
19
20 /**////
21 /// 删除留言
22 ///
23 /// 欲删除的留言的ID
24 /// 是否成功
25 bool Delete(int id);
26
27 /**////
28 /// 更新留言信息
29 ///
30 /// 留言实体类
31 /// 是否成功
32 bool Update(MessageInfo message);
33
34 /**////
35 /// 按ID取得留言信息
36 ///
37 /// 留言ID
38 /// 留言实体类
39 MessageInfo GetByID(int id);
40
41 /**////
42 /// 按分页取得留言信息
43 ///
44 /// 每页显示几条留言
45 /// 当前页码
46 /// 留言实体类集合
47 IList<MessageInfo> GetByPage(int pageSize,int pageNumber);
48 }
49}
ICommentDAL.cs:
1using System;
2using System.Collections.Generic;
3using System.Text;
4using NGuestBook.Entity;
5
6namespace NGuestBook.IDAL
7{
8 /**////
9 /// 数据访问层接口-评论
10 ///
11 public interface ICommentDAL
12 {
13 /**////
14 /// 插入评论
15 ///
16 /// 评论实体类
17 /// 是否成功
18 bool Insert(CommentInfo comment);
19
20 /**////
21 /// 删除评论
22 ///
23 /// 欲删除的评论的ID
24 /// 是否成功
25 bool Delete(int id);
26
27 /**////
28 /// 取得指定留言的全部评论
29 ///
30 /// 指定留言的ID
31 /// 评论实体类集合
32 IList<CommentInfo> GetByMessage(int messageId);
33 }
34}
基于.NET平台的分层架构实战(六)——依赖注入机制及IoC的设计与实现
我们设计的分层架构,层与层之间应该是松散耦合的。因为是单向单一调用,所以,这里的“松散耦合”实际是指上层类不能具体依赖于下层类,而应该依赖于下层提供的一个接口。这样,上层类不能直接实例化下层中的类,而只持有接口,至于接口所指变量最终究竟是哪一个类,则由依赖注入机制决定。
之所以这样做,是为了实现层与层之间的“可替换”式设计,例如,现在需要换一种方式实现数据访问层,只要这个实现遵循了前面定义的数据访问层接口,业务逻辑层和表示层不需要做任何改动,只需要改一下配置文件系统即可正常运行。另外,基于这种结构的系统,还可以实现并行开发。即不同开发人员可以专注于自己的层次,只有接口被定义好了,开发出来的东西就可以无缝连接。
在J2EE平台上,主要使用Spring框架实现依赖注入。这里,我们将自己做一个依赖注入容器。
依赖注入的理论基础是Abstract Factory设计模式,这里结合具体实例简单介绍一下。
上图以数据访问层为例,展示了Abstract Factory模式的应用。如图,现假设有针对Access和SQLServer两种数据库的数据访问层,它们都实现了数据访问层接口。每个数据访问层有自己的工厂,所有工厂都实现自IDALFactory接口。而客户类(这里就是业务逻辑层类)仅与工厂接口、数据访问层接口耦合,而与具体类无关,这样,只要通过配置文件确定实例化哪个工厂,就可以得到不同的数据访问层。
然而,这种设计虽然可行,但是代码比较冗余,因为这样需要为数据访问层的每一个实现编写一个工厂,业务逻辑层也一样。在以前,我们毫无办法,但是,.NET平台引入的反射机制,给我们提供了一种解决方案。使用反射,每个层只需要一个工厂,然后通过从配置文件中读出程序集的名称,动态加载相应类。另外,为了提高依赖注入机制的效率,这里引入缓存机制。下面来看具体实现。
配置
首先,需要在Web工程的Web.config文件的
节点下添加如下两个项:
这两个配置选项分别存储要应用的数据访问和也业务逻辑层的程序集名称。value目前是空,是因为目前还没有各个层次的具体实现。
实现缓存操作辅助类
为实现缓存操作,我们将缓存操作封装成一个辅助类,放在Utility工程下,具体代码如下:
CacheAccess.cs:
封装依赖注入代码
因为很多依赖注入代码非常相似,为了减少重复性代码,我们将可复用的代码先封装在一个类中。具体代码如下(这个类放在Factory工程下):
DependencyInjector.cs:
1using System;
2using System.Web;
3using System.Web.Caching;
4
5namespace NGuestBook.Utility
6{
7 /**////
8 /// 辅助类,用于缓存操作
9 ///
10 public sealed class CacheAccess
11 {
12 /**////
13 /// 将对象加入到缓存中
14 ///
15 /// 缓存键
16 /// 缓存对象
17 /// 缓存依赖项
18 public static void SaveToCache(string cacheKey, object cacheObject, CacheDependency dependency)
19 {
20 Cache cache = HttpRuntime.Cache;
21 cache.Insert(cacheKey, cacheObject, dependency);
22 }
23
24 /**////
25 /// 从缓存中取得对象,不存在则返回null
26 ///
27 /// 缓存键
28 /// 获取的缓存对象
29 public static object GetFromCache(string cacheKey)
30 {
31 Cache cache = HttpRuntime.Cache;
32
33 return cache[cacheKey];
34 }
35 }
36}
1using System;
2using System.Configuration;
3using System.Reflection;
4using System.Web;
5using System.Web.Caching;
6using NGuestBook.Utility;
7
8namespace NGuestBook.Factory
9{
10 /**////
11 /// 依赖注入提供者
12 /// 使用反射机制实现
13 ///
14 public sealed class DependencyInjector
15 {
16 /**////
17 /// 取得数据访问层对象
18 /// 首先检查缓存中是否存在,如果不存在,则利用反射机制返回对象
19 ///
20 /// 数据访问类名称
21 /// 数据访问层对象
22 public static object GetDALObject(string className)
23 {
24 /**////
25 /// 取得数据访问层名称,首先检查缓存,不存在则到配置文件中读取
26 /// 缓存依赖项为Web.Config文件
27 ///
28 object dal = CacheAccess.GetFromCache("DAL");
29 if (dal == null)
30 {
31 CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config"));
32 dal = ConfigurationManager.AppSettings["DAL"];
33 CacheAccess.SaveToCache("DAL", dal, fileDependency);
34 }
35
36 /**////
37 /// 取得数据访问层对象
38 ///
39 string dalName = (string)dal;
40 string fullClassName = dalName + "." + className;
41 object dalObject = CacheAccess.GetFromCache(className);
42 if (dalObject == null)
43 {
44 CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config"));
45 dalObject = Assembly.Load(dalName).CreateInstance(fullClassName);
46 CacheAccess.SaveToCache(className, dalObject, fileDependency);
47 }
48
49 return dalObject;
50 }
51
52 /**////
53 /// 取得业务逻辑层对象
54 /// 首先检查缓存中是否存在,如果不存在,则利用反射机制返回对象
55 ///
56 /// 业务逻辑类名称
57 /// 业务逻辑层对象
58 public static object GetBLLObject(string className)
59 {
60 /**////
61 /// 取得业务逻辑层名称,首先检查缓存,不存在则到配置文件中读取
62 /// 缓存依赖项为Web.Config文件
63 ///
64 object bll = CacheAccess.GetFromCache("BLL");
65 if (bll == null)
66 {
67 CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config"));
68 bll = ConfigurationManager.AppSettings["BLL"];
69 CacheAccess.SaveToCache("BLL", bll, fileDependency);
70 }
71
72 /**////
73 /// 取得业务逻辑层对象
74 ///
75 string bllName = (string)bll;
76 string fullClassName = bllName + "." + className;
77 object bllObject = CacheAccess.GetFromCache(className);
78 if (bllObject == null)
79 {
80 CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config"));
81 bllObject = Assembly.Load(bllName).CreateInstance(fullClassName);
82 CacheAccess.SaveToCache(className, bllObject, fileDependency);
83 }
84
85 return bllObject;
86 }
87 }
88}
实现工厂
下面使用两个辅助类,实现数据访问层工厂和业务逻辑层工厂。
DALFactory.cs
1using System;
2using NGuestBook.IDAL;
3
4namespace NGuestBook.Factory
5{
6 /**////
7 /// 数据访问层工厂,用于获取相应的数据访问层对象
8 /// 使用Abstract Factory设计模式+Facace设计模式+反射机制+缓存机制设计
9 ///
10 public sealed class DALFactory
11 {
12 /**////
13 /// 获取管理员数据访问层对象
14 ///
15 /// 管理员数据访问层对象
16 public static IAdminDAL CreateAdminDAL()
17 {
18 return (IAdminDAL)DependencyInjector.GetDALObject("AdminDAL");
19 }
20
21 /**////
22 /// 获取留言数据访问层对象
23 ///
24 /// 留言数据访问层对象
25 public static IMessageDAL CreateMessageDAL()
26 {
27 return (IMessageDAL)DependencyInjector.GetDALObject("MessageDAL");
28 }
29
30 /**////
31 /// 获取评论数据访问层对象
32 ///
33 /// 评论数据访问层对象
34 public static ICommentDAL CreateCommentDAL()
35 {
36 return (ICommentDAL)DependencyInjector.GetDALObject("CommentDAL");
37 }
38 }
39}
BLLFactory.cs
1using System;
2using NGuestBook.IBLL;
3
4namespace NGuestBook.Factory
5{
6 /**////
7 /// 业务逻辑层工厂,用于获取相应的业务逻辑层对象
8 /// 使用Abstract Factory设计模式+Facace设计模式+反射机制+缓存机制设计
9 ///
10 public sealed class BLLFactory
11 {
12 /**////
13 /// 获取管理员业务逻辑层对象
14 ///
15 /// 管理员业务逻辑层对象
16 public static IAdminBLL CreateAdminBLL()
17 {
18 return (IAdminBLL)DependencyInjector.GetBLLObject("AdminBLL");
19 }
20
21 /**////
22 /// 获取留言业务逻辑层对象
23 ///
24 /// 留言业务逻辑层对象
25 public static IMessageBLL CreateMessageBLL()
26 {
27 return (IMessageBLL)DependencyInjector.GetBLLObject("MessageBLL");
28 }
29
30 /**////
31 /// 获取评论业务逻辑层对象
32 ///
33 /// 评论业务逻辑层对象
34 public static ICommentBLL CreateCommentBLL()
35 {
36 return (ICommentBLL)DependencyInjector.GetBLLObject("CommentBLL");
37 }
38 }
39}
顾名思义,这种实现将使用Access作为后台数据库,而操作方式也是最基本的使用SQL命令。
在具体编写实现代码之前,我们需要做一些准备工作:
第一步,我们要将Access数据库搭建完成,具体做法如下。
在Web工程下新建一个文件夹,命名为AccessData,并在其中新建一个mdb文件(即Access数据库文件),按照前面介绍过的数据库设计构架,将数据表及表间关系建好,这里不再赘述。
第二步,我们要进行一些配置。
打开Web工程下的Web.config文件,在其中的appSettings节点下,添加如下键值:
第一条为Access的连接字符串,第二条为Access数据库文件的路径,其中“~”表示网站根目录。
第三步,新建一个工程。
我们要新建一个工程AccessDAL,用来存放Access数据访问层的代码。
准备工作做完了,现在来实现具体的代码。
1.编写数据访问助手类
因为很多数据访问操作流程很相似,所以,这里将一些可复用的代码抽取出来,编写成助手类,以此减少代码量,提高代码复用性。
这个助手类放在AccessDAL下,叫AccessDALHelper,主要负责Access数据库的访问。它包括三个方法:
GetConnectionString:从配置文件中读取配置项,组合成连接字符串。
ExecuteSQLNonQuery:执行指定SQL语句,不返回任何值,一般用于Insert,Delete,Update命令。
ExecuteSQLDataReader:执行SQL语句返回查询结果,一般用于Select命令。
具体代码如下:
AccessDALHelper.cs:
2.实现具体的数据访问操作类
因为前面已经定义了数据访问层接口,所以实现数据访问操作类就是很机械的工作了。下面仅以Admin的数据访问操作类为例:
AdminDAL:
1using System;
2using System.Web;
3using System.Web.Caching;
4using System.Configuration;
5using System.Data;
6using System.Data.OleDb;
7using NGuestBook.Utility;
8
9namespace NGuestBook.AccessDAL
10{
11 /**////
12 /// Access数据库操作助手
13 ///
14 public sealed class AccessDALHelper
15 {
16 /**////
17 /// 读取Access数据库的连接字符串
18 /// 首先从缓存里读取,如果不存在则到配置文件中读取,并放入缓存
19 ///
20 /// Access数据库的连接字符串
21 private static string GetConnectionString()
22 {
23 if (CacheAccess.GetFromCache("AccessConnectionString") != null)
24 {
25 return CacheAccess.GetFromCache("AccessConnectionString").ToString();
26 }
27 else
28 {
29 string dbPath = ConfigurationManager.AppSettings["AccessPath"];
30 string dbAbsolutePath = HttpContext.Current.Server.MapPath(dbPath);
31 string connectionString = ConfigurationManager.AppSettings["AccessConnectionString"];
32
33 CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config"));
34 CacheAccess.SaveToCache("AccessConnectionString", connectionString.Replace("{DBPath}", dbAbsolutePath), fileDependency);
35
36 return connectionString.Replace("{DBPath}", dbAbsolutePath);
37 }
38 }
39
40 /**////
41 /// 执行SQL语句并且不返回任何值
42 ///
43 /// 所执行的SQL命令
44 /// 参数集合
45 public static void ExecuteSQLNonQuery(string SQLCommand,OleDbParameter[] parameters)
46 {
47 OleDbConnection connection = new OleDbConnection(GetConnectionString());
48 OleDbCommand command = new OleDbCommand(SQLCommand, connection);
49
50 for (int i = 0; i < parameters.Length; i++)
51 {
52 command.Parameters.Add(parameters[i]);
53 }
54
55 connection.Open();
56 command.ExecuteNonQuery();
57 connection.Close();
58 }
59
60 /**////
61 /// 执行SQL语句并返回包含查询结果的DataReader
62 ///
63 /// 所执行的SQL命令
64 /// 参数集合
65 ///
66 public static OleDbDataReader ExecuteSQLDataReader(string SQLCommand,OleDbParameter[] parameters)
67 {
68 OleDbConnection connection = new OleDbConnection(GetConnectionString());
69 OleDbCommand command = new OleDbCommand(SQLCommand, connection);
70
71 for (int i = 0; i < parameters.Length; i++)
72 {
73 command.Parameters.Add(parameters[i]);
74 }
75
76 connection.Open();
77 OleDbDataReader dataReader = command.ExecuteReader();
78 //connection.Close();
79
80 return dataReader;
81 }
82 }
83}
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Data;
5using System.Data.OleDb;
6using NGuestBook.IDAL;
7using NGuestBook.Entity;
8
9namespace NGuestBook.AccessDAL
10{
11 public class AdminDAL : IAdminDAL
12 {
13 /**////
14 /// 插入管理员
15 ///
16 /// 管理员实体类
17 /// 是否成功
18 public bool Insert(AdminInfo admin)
19 {
20 string SQLCommand = "insert into [TAdmin]([Name],[Password]) values(@name,@password)";
21 OleDbParameter[] parameters ={
22 new OleDbParameter("name",admin.Name),
23 new OleDbParameter("password",admin.Password)
24 };
25
26 try
27 {
28 AccessDALHelper.ExecuteSQLNonQuery(SQLCommand, parameters);
29 return true;
30 }
31 catch
32 {
33 return false;
34 }
35 }
36
37 /**////
38 /// 删除管理员
39 ///
40 /// 欲删除的管理员的ID
41 /// 是否成功
42 public bool Delete(int id)
43 {
44 string SQLCommand = "delete from [TAdmin] where [ID]=@id";
45 OleDbParameter[] parameters ={
46 new OleDbParameter("id",id)
47 };
48
49 try
50 {
51 AccessDALHelper.ExecuteSQLNonQuery(SQLCommand, parameters);
52 return true;
53 }
54 catch
55 {
56 return false;
57 }
58 }
59
60 /**////
61 /// 更新管理员信息
62 ///
63 /// 管理员实体类
64 /// 是否成功
65 public bool Update(AdminInfo admin)
66 {
67 string SQLCommand = "update [TAdmin] set [Name]=@name,[Password]=@password where [ID]=@id";
68 OleDbParameter[] parameters ={
69 new OleDbParameter("id",admin.ID),
70 new OleDbParameter("name",admin.Name),
71 new OleDbParameter("password",admin.Password)
72 };
73
74 try
75 {
76 AccessDALHelper.ExecuteSQLNonQuery(SQLCommand, parameters);
77 return true;
78 }
79 catch
80 {
81 return false;
82 }
83 }
84
85 /**////
86 /// 按ID取得管理员信息
87 ///
88 /// 管理员ID
89 /// 管理员实体类
90 public AdminInfo GetByID(int id)
91 {
92 string SQLCommand = "select * from [TAdmin] where [ID]=@id";
93 OleDbParameter[] parameters ={
94 new OleDbParameter("id",id)
95 };
96
97 try
98 {
99 OleDbDataReader dataReader = AccessDALHelper.ExecuteSQLDataReader(SQLCommand, parameters);
100 if (!dataReader.HasRows)
101 {
102 throw new Exception();
103 }
104
105 AdminInfo admin = new AdminInfo();
106 dataReader.Read();
107 admin.ID=(int)dataReader["ID"];
108 admin.Name=(string)dataReader["Name"];
109 admin.Password=(string)dataReader["Password"];
110
111 return admin;
112 }
113 catch
114 {
115 return null;
116 }
117 }
118
119 /**////
120 /// 按用户名及密码取得管理员信息
121 ///
122 /// 用户名
123 /// 密码
124 /// 管理员实体类,不存在时返回null
125 public AdminInfo GetByNameAndPassword(string name, string password)
126 {
127 string SQLCommand = "select * from [TAdmin] where [Name]=@name and [Password]=@password";
128 OleDbParameter[] parameters ={
129 new OleDbParameter("name",name),
130 new OleDbParameter("password",password),
131 };
132
133 try
134 {
135 OleDbDataReader dataReader = AccessDALHelper.ExecuteSQLDataReader(SQLCommand, parameters);
136 if (!dataReader.HasRows)
137 {
138 throw new Exception();
139 }
140
141 AdminInfo admin = new AdminInfo();
142 dataReader.Read();
143 admin.ID = (int)dataReader["ID"];
144 admin.Name = (string)dataReader["Name"];
145 admin.Password = (string)dataReader["Password"];
146
147 return admin;
148 }
149 catch
150 {
151 return null;
152 }
153 }
154
155 /**////
156 /// 按管理员名取得管理员信息
157 ///
158 /// 管理员名
159 /// 管理员实体类
160 public AdminInfo GetByName(string name)
161 {
162 string SQLCommand = "select * from [TAdmin] where [Name]=@name";
163 OleDbParameter[] parameters ={
164 new OleDbParameter("name",name),
165 };
166
167 try
168 {
169 OleDbDataReader dataReader = AccessDALHelper.ExecuteSQLDataReader(SQLCommand, parameters);
170 if (!dataReader.HasRows)
171 {
172 throw new Exception();
173 }
174
175 AdminInfo admin = new AdminInfo();
176 dataReader.Read();
177 admin.ID = (int)dataReader["ID"];
178 admin.Name = (string)dataReader["Name"];
179 admin.Password = (string)dataReader["Password"];
180
181 return admin;
182 }
183 catch
184 {
185 return null;
186 }
187 }
188
189 /**////
190 /// 取得全部管理员信息
191 ///
192 /// 管理员实体类集合
193 public IList<AdminInfo> GetAll()
194 {
195 string SQLCommand = "select * from [TAdmin]";
196 try
197 {
198 OleDbDataReader dataReader = AccessDALHelper.ExecuteSQLDataReader(SQLCommand, null);
199 if (!dataReader.HasRows)
200 {
201 throw new Exception();
202 }
203
204 IList<AdminInfo> adminCollection = new List<AdminInfo>();
205 int i = 0;
206 while (dataReader.Read())
207 {
208 AdminInfo admin = new AdminInfo();
209 admin.ID = (int)dataReader["ID"];
210 admin.Name = (string)dataReader["Name"];
211 admin.Password = (string)dataReader["Password"];
212
213 adminCollection.Add(admin);
214 i++;
215 }
216
217 return adminCollection;
218 }
219 catch
220 {
221 return null;
222 }
223 }
224 }
225}
可以看到,这里主要包括三种类型的操作,一种是修改型,如Insert;一种是返回单个实体类型,如GetByID;还有一种是返回实体类集合型,如GetAll。
MessageDAL和CommentDAL的实现非常相似,在这里不再赘述。
在上一篇中,讨论了使用SQL构建数据访问层的方法,并且针对的是Access数据库。而这一篇中,将要创建一个针对SQLServer数据库的数据访问层,并且配合存储过程实现。
曾经有朋友问我使用SQL和存储过程在效率上的差别,惭愧的是我对这方面没有研究,也没有实际做过测试。通过查阅资料,发现在一般情况下,存储过程的效率由于使用SQL,但是也不绝对,也发现有的朋友测试时发现在特定情况下SQL的效率优于存储过程,所以这个问题不能一概而论。
好,废话不多说,这里先列出使用存储过程构建数据访问层的一般步骤:
1.创建新工程
2.创建数据库
3.编写相应存储过程
4.编写数据库辅助类
5.实现数据访问层
创建新工程
在开始所有开发工作前,我们需要在解决方案下新建一个工程,叫SQLServerDAL,用于存放所有SQLServer数据访问层的代码。
创建数据库
首先,我们要根据前文设计的数据库,在SQLServer中创建相应的数据库及数据表。我使用的是SQLServer2005,使用企业管理器创建,创建方法不再赘述。
编写存储过程
数据库创建完成后,我们就要编写存储过程了。由于数据访问层接口已经确定,所以需要哪些存储过程也很好确定。例如数据访问层接口中有一个添加管理员方法,那么就一定有一个存储过程实现这个功能。
还是以管理员模块为例,经过简单分析,需要一下存储过程:
插入管理员记录
删除管理员记录
更新管理员信息
按ID取得管理员记录
按用户名及密码取得管理员记录
按用户名取得管理员记录
取得全部管理员记录
创建这些存储过程的SQL代码如下:
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:
-- Create date: <2008-07-04>
-- Description: <插入管理员记录>
-- =============================================
CREATE PROCEDURE [dbo].[Pr_InsertAdmin]
(
@Name Nvarchar(20),
@Password Nvarchar(50)
)
AS
INSERT INTO TAdmin
(
[Name],
[Password]
)
VALUES
(
@Name,
@Password
)
1set ANSI_NULLS ON
2set QUOTED_IDENTIFIER ON
3GO
4-- =============================================
5-- Author:
6-- Create date: <2008-07-04>
7-- Description: <删除管理员记录>
8-- =============================================
9CREATE PROCEDURE [dbo].[Pr_DeleteAdmin]
10(
11 @ID Int
12)
13AS
14DELETE FROM TAdmin
15WHERE [ID]=@ID
1set ANSI_NULLS ON
2set QUOTED_IDENTIFIER ON
3GO
4-- =============================================
5-- Author:
6-- Create date: <2008-07-04>
7-- Description: <修改管理员记录>
8-- =============================================
9CREATE PROCEDURE [dbo].[Pr_UpdateAdmin]
10(
11 @ID Int,
12 @Name Nvarchar(20),
13 @Password Nvarchar(50)
14)
15AS
16UPDATE TAdmin
17SET
18[Name]=@Name,
19[Password]=@Password
20WHERE [ID]=@ID
1set ANSI_NULLS ON
2set QUOTED_IDENTIFIER ON
3GO
4-- =============================================
5-- Author:
6-- Create date: <2008-07-04>
7-- Description: <按ID取得管理员信息>
8-- =============================================
9CREATE PROCEDURE [dbo].[Pr_GetAdminByID]
10(
11 @ID Int
12)
13AS
14SELECT * FROM TAdmin
15WHERE [ID]=@ID
1set ANSI_NULLS ON
2set QUOTED_IDENTIFIER ON
3GO
4-- =============================================
5-- Author:
6-- Create date: <2008-07-04>
7-- Description: <按用户名及密码取得管理员信息>
8-- =============================================
9CREATE PROCEDURE [dbo].[Pr_GetAdminByNameAndPassword]
10(
11 @Name Nvarchar(20),
12 @Password Nvarchar(50)
13)
14AS
15SELECT * FROM TAdmin
16WHERE [Name]=@Name
17AND [Password]=@Password
1set ANSI_NULLS ON
2set QUOTED_IDENTIFIER ON
3GO
4-- =============================================
5-- Author:
6-- Create date: <2008-07-04>
7-- Description: <按用户名取得管理员信息>
8-- =============================================
9CREATE PROCEDURE [dbo].[Pr_GetAdminByName]
10(
11 @Name Nvarchar(20)
12)
13AS
14SELECT * FROM TAdmin
15WHERE [Name]=@Name
1set ANSI_NULLS ON
2set QUOTED_IDENTIFIER ON
3GO
4-- =============================================
5-- Author:
6-- Create date: <2008-07-04>
7-- Description: <取得全部管理员信息>
8-- =============================================
9CREATE PROCEDURE [dbo].[Pr_GetAllAdmin]
10AS
11SELECT * FROM TAdmin
编写数据库辅助类
由于访问数据库的代码很相似,这里我们仍需要编写一个数据库辅助类,来将常用代码封装起来,方便复用。虽然在这里只使用到了存储过程,但是为了扩展性考虑,这个数据库辅助类仍然包含了通过SQL访问数据库的方法。具体实现如下:
SQLServerDALHelper.cs:
1using System;
2using System.Collections.Generic;
3using System.Configuration;
4using System.Data;
5using System.Data.SqlClient;
6
7namespace NGuestBook.SQLServerDAL
8{
9 /**////
10 /// SQLServer数据库操作助手
11 ///
12 public sealed class SQLServerDALHelper
13 {
14 /**////
15 /// 用于连接SQLServer数据库的连接字符串,存于Web.config中
16 ///
17 private static readonly string _sqlConnectionString = ConfigurationManager.AppSettings["SQLServerConnectionString"];
18
19 /**////
20 /// 执行SQL命令,不返回任何值
21 ///
22 /// SQL命令
23 public static void ExecuteSQLNonQurey(string sql)
24 {
25 SqlConnection connection = new SqlConnection(_sqlConnectionString);
26 SqlCommand command = new SqlCommand(sql,connection);
27 connection.Open();
28 command.ExecuteNonQuery();
29 connection.Close();
30 }
31
32 /**////
33 /// 执行SQL命令,并返回SqlDataReader
34 ///
35 /// SQL命令
36 /// 包含查询结果的SqlDataReader
37 public static SqlDataReader ExecuteSQLReader(string sql)
38 {
39 SqlConnection connection = new SqlConnection(_sqlConnectionString);
40 SqlCommand command = new SqlCommand(sql, connection);
41 connection.Open();
42 SqlDataReader sqlReader = command.ExecuteReader();
43 //connection.Close();
44
45 return sqlReader;
46 }
47
48 /**////
49 /// 执行存储过程,不返回任何值
50 ///
51 /// 存储过程名
52 /// 参数
53 public static void ExecuteProcedureNonQurey(string storedProcedureName,IDataParameter[] parameters)
54 {
55 SqlConnection connection = new SqlConnection(_sqlConnectionString);
56 SqlCommand command = new SqlCommand(storedProcedureName,connection);
57 command.CommandType = CommandType.StoredProcedure;
58 if (parameters != null)
59 {
60 foreach (SqlParameter parameter in parameters)
61 {
62 command.Parameters.Add(parameter);
63 }
64 }
65 connection.Open();
66 command.ExecuteNonQuery();
67 connection.Close();
68 }
69
70 /**////
71 /// 执行存储,并返回SqlDataReader
72 ///
73 /// 存储过程名
74 /// 参数
75 /// 包含查询结果的SqlDataReader
76 public static SqlDataReader ExecuteProcedureReader(string storedProcedureName,IDataParameter[] parameters)
77 {
78 SqlConnection connection = new SqlConnection(_sqlConnectionString);
79 SqlCommand command = new SqlCommand(storedProcedureName,connection);
80 command.CommandType = CommandType.StoredProcedure;
81 if (parameters != null)
82 {
83 foreach (SqlParameter parameter in parameters)
84 {
85 command.Parameters.Add(parameter);
86 }
87 }
88 connection.Open();
89 SqlDataReader sqlReader = command.ExecuteReader();
90 //connection.Close();
91
92 return sqlReader;
93 }
94 }
95}
实现数据访问层
最后仍以管理员模块为例,看一下具体数据访问层的实现。
AdminDAL.cs:
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Data;
5using System.Data.SqlClient;
6using NGuestBook.IDAL;
7using NGuestBook.Entity;
8
9namespace NGuestBook.SQLServerDAL
10{
11 public class AdminDAL : IAdminDAL
12 {
13 /**////
14 /// 插入管理员
15 ///
16 /// 管理员实体类
17 /// 是否成功
18 public bool Insert(AdminInfo admin)
19 {
20 SqlParameter[] parameters =
21 {
22 new SqlParameter("@Name",SqlDbType.NVarChar),
23 new SqlParameter("@Password",SqlDbType.NVarChar)
24 };
25 parameters[0].Value = admin.Name;
26 parameters[1].Value = admin.Password;
27 try
28 {
29 SQLServerDALHelper.ExecuteProcedureNonQurey("Pr_InsertAdmin", parameters);
30 return true;
31 }
32 catch
33 {
34 return false;
35 }
36 }
37
38 /**////
39 /// 删除管理员
40 ///
41 /// 欲删除的管理员的ID
42 /// 是否成功
43 public bool Delete(int id)
44 {
45 SqlParameter[] parameters =
46 {
47 new SqlParameter("@ID",SqlDbType.Int)
48 };
49 parameters[0].Value = id;
50 try
51 {
52 SQLServerDALHelper.ExecuteProcedureNonQurey("Pr_DeleteAdmin", parameters);
53 return true;
54 }
55 catch
56 {
57 return false;
58 }
59 }
60
61 /**////
62 /// 更新管理员信息
63 ///
64 /// 管理员实体类
65 /// 是否成功
66 public bool Update(AdminInfo admin)
67 {
68 SqlParameter[] parameters =
69 {
70 new SqlParameter("@ID",SqlDbType.Int),
71 new SqlParameter("@Name",SqlDbType.NVarChar),
72 new SqlParameter("@Password",SqlDbType.NVarChar)
73 };
74 parameters[0].Value = admin.ID;
75 parameters[1].Value = admin.Name;
76 parameters[2].Value = admin.Password;
77 try
78 {
79 SQLServerDALHelper.ExecuteProcedureNonQurey("Pr_UpdateAdmin", parameters);
80 return true;
81 }
82 catch
83 {
84 return false;
85 }
86 }
87
88 /**////
89 /// 按ID取得管理员信息
90 ///
91 /// 管理员ID
92 /// 管理员实体类
93 public AdminInfo GetByID(int id)
94 {
95 SqlParameter[] parameters =
96 {
97 new SqlParameter("@ID",SqlDbType.Int)
98 };
99 parameters[0].Value = id;
100 SqlDataReader dataReader = null;
101 try
102 {
103 dataReader = SQLServerDALHelper.ExecuteProcedureReader("GetAdminByID", parameters);
104 dataReader.Read();
105 AdminInfo admin = new AdminInfo();
106 admin.ID = (int)dataReader["ID"];
107 admin.Name = (string)dataReader["Name"];
108 admin.Password = (string)dataReader["Password"];
109
110 return admin;
111 }
112 catch
113 {
114 return null;
115 }
116 finally
117 {
118 dataReader.Close();
119 }
120 }
121
122 /**////
123 /// 按用户名及密码取得管理员信息
124 ///
125 /// 用户名
126 /// 密码
127 /// 管理员实体类,不存在时返回null
128 public AdminInfo GetByNameAndPassword(string name, string password)
129 {
130 SqlParameter[] parameters =
131 {
132 new SqlParameter("@Name",SqlDbType.NVarChar),
133 new SqlParameter("@Password",SqlDbType.NVarChar)
134 };
135 parameters[0].Value = name;
136 parameters[1].Value = password;
137 SqlDataReader dataReader = null;
138 try
139 {
140 dataReader = SQLServerDALHelper.ExecuteProcedureReader("GetAdminByNameAndPassword", parameters);
141 dataReader.Read();
142 AdminInfo admin = new AdminInfo();
143 admin.ID = (int)dataReader["ID"];
144 admin.Name = (string)dataReader["Name"];
145 admin.Password = (string)dataReader["Password"];
146
147 return admin;
148 }
149 catch
150 {
151 return null;
152 }
153 finally
154 {
155 dataReader.Close();
156 }
157 }
158
159 /**////
160 /// 按管理员名取得管理员信息
161 ///
162 /// 管理员名
163 /// 管理员实体类
164 public AdminInfo GetByName(string name)
165 {
166 SqlParameter[] parameters =
167 {
168 new SqlParameter("@Name",SqlDbType.NVarChar)
169 };
170 parameters[0].Value = name;
171 SqlDataReader dataReader = null;
172 try
173 {
174 dataReader = SQLServerDALHelper.ExecuteProcedureReader("GetAdminByName", parameters);
175 dataReader.Read();
176 AdminInfo admin = new AdminInfo();
177 admin.ID = (int)dataReader["ID"];
178 admin.Name = (string)dataReader["Name"];
179 admin.Password = (string)dataReader["Password"];
180
181 return admin;
182 }
183 catch
184 {
185 return null;
186 }
187 finally
188 {
189 dataReader.Close();
190 }
191 }
192
193 /**////
194 /// 取得全部管理员信息
195 ///
196 /// 管理员实体类集合
197 public IList<AdminInfo> GetAll()
198 {
199 SqlDataReader dataReader = null;
200 try
201 {
202 dataReader = SQLServerDALHelper.ExecuteProcedureReader("GetAllAdmin", null);
203 IList<AdminInfo> adminCollection=new List<AdminInfo>();
204 while (dataReader.Read())
205 {
206 AdminInfo admin = new AdminInfo();
207 admin.ID = (int)dataReader["ID"];
208 admin.Name = (string)dataReader["Name"];
209 admin.Password = (string)dataReader["Password"];
210 adminCollection.Add(admin);
211 }
212
213 return adminCollection;
214 }
215 catch
216 {
217 return null;
218 }
219 finally
220 {
221 dataReader.Close();
222 }
223 }
224 }
225}