通过浏览博客园的文章发现,很多朋友对分层架构特别感兴趣,刚好我刚做完的毕业设计就是专门研究.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.总结
当然,以上只是初步计划,在写文章的过程中可能会根据具体情况适当调整,但是内容大体就是这些。
这个文章系列不会对所用到的
设计数据表之前,首先进行实体和关系的识别与确定。
通过需求分析,可以观察得出,本项目的实体有:管理员(不包括超级管理员),留言,评论。本项目的关系有:留言与评论间的一对多关系。
进一步,数据库各表的设计如下:
管理员表(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
IsPass varchar(10) 是否通过验证 NotNull
评论表(TComment)
ID int 评论ID NotNull 主键,自增
Content text 评论内容 NotNull
Time datetime 发表评论时间 NotNull
MessageID int 所属留言的ID 外键
架构概要设计
这篇中理论比较多,但是它是整个架构的基础,可以帮助朋友们对将要实现的项目架构及要遵循的原则有一个整体的了解。当然,在后续文章中,将主要讨论Demo项目的实际开发过程,那时,这些思想和理论性的东西将得到体现。
实体类的设计与实现
如上图所示,在初始阶段,整个系统包括6个工程,它们的职责是这样的:
Web——表示层
Entity——存放实体类
Factory——存放和依赖注入及IoC相关的类
IBLL——存放业务逻辑层接口族
IDAL——存放数据访问层接口族
Utility——存放各种工具类及辅助类
这只是一个初期架构,主要是将整个系统搭一个框架,在后续开发中,将会有其他工程被陆陆续续添加进来。
我们的实体类将放在Entity工程下,这里包括三个文件:AdminInfo.cs,MessageInfo.cs,CommentInfo.cs,分别是管理员实体类、留言实体类和评论实体类。具体代码如下:
AdminInfo.cs:
AdminInfo
1using System;
2
3namespace NGuestBook.Entity
4{
5 /**//// <summary>
6 /// 实体类-管理员
7 /// </summary>
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:
MessageInfo
1using System;
2
3namespace NGuestBook.Entity
4{
5 /**//// <summary>
6 /// 实体类-留言
7 /// </summary>
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:
CommentInfo
1using System;
2
3namespace NGuestBook.Entity
4{
5 /**//// <summary>
6 /// 实体类-评论
7 /// </summary>
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
大家可以看出,实体类的代码很简单,仅仅是负责实体的表示和数据的传递,不包含任何逻辑性内容。
接口的设计与实现
31 /// <returns>评论实体类集合</returns>
32 IList<CommentInfo> GetByMessage(int messageId);
33 }
34}
由业务逻辑确定数据访问操作
IAdminBLL需要的数据访问操作:插入管理员,删除管理员,更新管理员信息,按ID取得管理员信息,按登录名与密码取得管理员,取得全部管理员
IMessageBLL需要的数据访问操作:插入留言,删除留言,更新留言信息,按ID取得留言信息,按分页取得留言
ICommentBLL需要的数据访问操作:插入评论,删除评论,按留言取得全部评论
另外,添加管理员时需要验证是否存在同名管理员,所以需要添加一个“按登录名取得管理员”。
这两个配置选项分别存储要应用的数据访问和也业务逻辑层的程序集名称。value目前是空,是因为目前还没有各个层次的具体实现。
实现缓存操作辅助类
为实现缓存操作,我们将缓存操作封装成一个辅助类,放在Utility工程下,具体代码如下:
32 IList<CommentInfo> GetByMessage(int messageId);
33 }
34}
由业务逻辑确定数据访问操作
IAdminBLL需要的数据访问操作:插入管理员,删除管理员,更新管理员信息,按ID取得管理员信息,按登录名与密码取得管理员,取得全部管理员
IMessageBLL需要的数据访问操作:插入留言,删除留言,更新留言信息,按ID取得留言信息,按分页取得留言
ICommentBLL需要的数据访问操作:插入评论,删除评论,按留言取得全部评论
另外,添加管理员时需要验证是否存在同名管理员,所以需要添加一个“按登录名取得管理员”。
.NET平台依赖注入机制及IoC的设计与实现
| <add key="DAL" value=""/> <add key="BLL" value=""/> |
实现缓存操作辅助类
为实现缓存操作,我们将缓存操作封装成一个辅助类,放在Utility工程下,具体代码如下:
| using System; using System.Web; using System.Web.Caching; namespace NGuestBook.Utility { /**//// <summary> /// 辅助类,用于缓存操作 /// </summary> public sealed class CacheAccess { /**//// <summary> /// 将对象加入到缓存中 /// </summary> /// <param name="cacheKey">缓存键</param> /// <param name="cacheObject">缓存对象</param> /// <param name="dependency">缓存依赖项</param> public static void SaveToCache(string cacheKey, object cacheObject, CacheDependency dependency) { Cache cache = HttpRuntime.Cache; cache.Insert(cacheKey, cacheObject, dependency); } /**//// <summary> /// 从缓存中取得对象,不存在则返回null /// </summary> /// <param name="cacheKey">缓存键</param> /// <returns>获取的缓存对象</returns> public static object GetFromCache(string cacheKey) { Cache cache = HttpRuntime.Cache; return cache[cacheKey]; } } } 基于.NET平台的分层架构实战(转载)
封装依赖注入代码
因为很多依赖注入代码非常相似,为了减少重复性代码,我们将可复用的代码先封装在一个类中。具体代码如下(这个类放在Factory工程下):
下面使用两个辅助类,实现数据访问层工厂和业务逻辑层工厂。
|