三层架构
为什么要用三层
说到三层,先来说一说两层结构。两层结构将界面展示、业务逻辑、数据访问等都写到一起,如果用户需求变化,就需要对整个项目进行大量修改,系统的维护和升级极其不利;而且界面层直接访问数据库,还会有安全隐患。结构如下图所示:
所以基于两层结构的局限性,三层结构就出现了。三层结构符合“高内聚、低耦合”的特点,每个层职责明确。利用分层,降低了层间依赖,使系统的耦合更加松散,从而使系统更加容易维护和复用。
比如:如果需求有变化,只需要更改相应的业务逻辑层;或者要改变数据库的时候,只需要将原来的数据访问层替换掉或者增加新的就可以了,而不需要牵扯到整个项目。
三层架构虽好,但是也不是每个项目都必须采用这种结构,三层结构用于比较复杂的大型系统,如果系统比较小,则没必要将问题复杂化。
何为三层
三层由显示层(UI)、业务逻辑层(BLL)、数据访问层(DAL)组成。
1.显示层(UI)
职责:①向用户展示特定的业务数据
②采集用户的信息和操作
原则:用户至上,兼顾简洁
2.业务逻辑层(BLL)
职责:① 从UI中获取用户指令和数据,执行业务逻辑
②从UI中获取用户指令和数据,通过DAL写入数据源
③从DAL中获取数据,以供 UI 显示用
机制:① UI –> BLL –> UI
② UI –> BLL –> DAL –> BLL –> UI
3.数据访问层(DAL)
作用:跟数据源打交道
职责:①执行对数据的操作(增删改查)
4.数据对象层
数据对象层包含了项目需要使用的数据对象,用数据对象来传递数据,它避免了各个层的交叉引用。
一般一个表对应一个数据对象。
引用关系
上图已经展示了它们三者之间的引用关系。UI层–> BLL层 –> DAL层,而它们都引用数据对象层。
代码展示
下面以一个登录的小例子,展示一下三层是如何具体使用的。
UI层:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnLogin_Click(object sender, EventArgs e)
{
//获取输入的用户名和密码
string userName = txtUserName.Text.Trim();
string userPassword = txtPassword.Text;
Login.BLL.LoginManager mgr = new Login.BLL.LoginManager();
//调用B层的登录方法,从B层返回user的信息
UserInfo user = mgr.Login(userName, userPassword);
MessageBox.Show("登录用户:" + user.UserName);
}
}
BLL层:
public class LoginManager
{
public UserInfo Login(string userName,string password)
{
//实例化UserDao
UserDAO uDao = new UserDAO();
//调用UserDao的查询用户方法
UserInfo user = uDao.SelectUser(userName, password);
//如果用户存在,就调用ScoreDao的更新积分方法,给用户加10积分;否则就抛出异常
if (user != null)
{
ScoreDAO sDao = new ScoreDAO();
//调用ScoreDao的更新积分方法
sDao.UpdateScore(userName, 10);
return user;
}
else
{
throw new Exception("登录失败。");
}
}
}
DAL层:
public class UserDAO
{
/// <summary>
/// 查询用户是否存在并返回用户信息
/// </summary>
/// <param name="userName">用户名</param>
/// <param name="password">密码</param>
/// <returns></returns>
public UserInfo SelectUser(string userName,string password)
{
using (SqlConnection conn = new SqlConnection(DbUtil.ConnString))
{
SqlCommand cmd = conn.CreateCommand();
//获取执行的SQL语句 或表名 或存储过程名
cmd.CommandText = @"SELECT ID,UserName,Password,Email
FROM USERS WHERE aaa@qq.com AND aaa@qq.com";
//指示执行的是存储过程还是sql语句,默认执行语句
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(new SqlParameter("@UserName", userName));
cmd.Parameters.Add(new SqlParameter("@Password", password));
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
UserInfo user = null;
while (reader.Read())
{
if (user == null)
{
user = new UserInfo();
}
user.ID = reader.GetInt32(0);
user.UserName = reader.GetString(1);
user.Password = reader.GetString(2);
if (!reader.IsDBNull(3))
{
user.Email = reader.GetString(3);
}
}
return user;
}
}
}
public class ScoreDAO
{
/// <summary>
/// 更新用户积分
/// </summary>
/// <param name="userName">用户名</param>
/// <param name="value">要增长的积分</param>
public void UpdateScore(string userName,int value)
{
using (SqlConnection conn = new SqlConnection(DbUtil.ConnString))
{
SqlCommand cmd = conn.CreateCommand();
//查询Scores表中是否已有用户信息
cmd.CommandText = @"SELECT * FROM Scores WHERE UserName aaa@qq.com";
cmd.CommandType = System.Data.CommandType.Text;
cmd.Parameters.Add(new SqlParameter("@UserName", userName));
cmd.Parameters.Add(new SqlParameter("@Score", value));
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
//如果Scores表中用户积分已存在就在原来的基础上加10份;否则就插入新的用户积分数据
if (reader.Read())
{
//在用户原有积分基础上加10分
cmd.CommandText = @"UPDATE Scores SET Score+=10 WHERE UserName = @UserName";
//关闭reader
reader.Close();
//执行SQL语句
cmd.ExecuteNonQuery();
}
else
{
reader.Close();
//插入新的用户积分数据
cmd.CommandText = @"INSERT INTO SCORES(UserName,Score) Values(@UserName,@Score)";
cmd.ExecuteNonQuery();
}
}
}
}