(八)Asp.NET中三层架构的应用
分类:
IT文章
•
2025-01-27 18:40:14
一、 什么是三层架构?
生活中的三层

初始结构:
在现实生活中,如果老王开了一家饭店,前期顾客比较少,自己来招待客人、在后厨炒菜、每天去市场采购食材。但是随着顾客量的增加,饭店的生意越来越兴隆,自己一个人单干忙的不可开交。就好比我们的软件系统一样,我们的用户是浏览我们的网页的,主要的功能是体现在UI层面,用户和系统产生交互,UI层面需要接收用户的数据信息、处理逻辑、访问数据库等等。那么如果我们的业务足够复杂和庞大,我们把所有的代码都集中在一个地方,那么对于项目后期的扩展或者维护会变得异常困难。


改变成三层:
为了缓解人手不足的局面,老王便招来了若干个服务员、厨师和采购员,他们各司其职,每个人都有自己的专职工作,这样合理的分功让饭店的日常经营变得十分顺利。那么对于我们的软件开发也应该是同样的道理,我们应该按照不同的处理的业务模块来进行合理的划分,一般我们会把系统架构分为UI表现层、业务逻辑层、数据访问层这样的三层,这就是经典的三层架构。

使用三层架构的目的:
目的:解耦
服务员(UI层)请假——另找服务员
厨师(BLL层)辞职——招聘另一个厨师
采购员(DAL层)辞职——招聘另一个采购员
顾客反映:
你们店服务态度不好——服务员的问题。开除服务员
你们店菜里有虫子——厨师的问题。换厨师
UI(表现层):主要是指与用户交互的界面。用于接收用户输入的数据和显示处理后用户需要的数据。
BLL(业务逻辑层):UI层和DAL层之间的桥梁。实现业务逻辑。业务逻辑具体包含:验证、计算、业务规则等等。
DAL(数据访问层):与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交给业务层,同时将业务层处理的数据保存到数据库。(当然这些操作都是基于UI层的。用户的需求反映给界面(UI),UI反映给BLL,BLL反映给DAL,DAL进行数据的操作,操作后再一一返回,直到将用户所需数据反馈给用户)
那么在三层架构中各层之间为了能够处理一个统一的功能,相互直接是如何依赖的呢?

我们从UI表现层出发依次从上往下来看,表现层需要调用业务逻辑层,业务逻辑层需要调用数据访问层,而数据的在每个层的传输需要通过Model实体层。

那么引入了三层架构之后我们的请求流程变为了用户在UI表现层提交数据,调用业务逻辑层中的方法来处理当前功能的业务逻辑,如果要和数据库做交互,那么需要在业务逻辑层中调用数据访问层来对数据进行操作,数据访问层最终会有一个处理的结果,有可能是操作成功失败,也有可能是读取数据,数据访问将这个结果返回给业务逻辑层,业务逻辑层获取这个结果之后将它返回给UI表现层,这样三层架构的请求流程就结束了。
接下来,我们来演示三层架构的实现具体步骤:
二、 三层架构综合应用
1、在项目中添加类库MODEL
在Model层中添加项目所需的实体类:如:Product类和ProductCategory类
/// <summary>
/// 产品
/// </summary>
public class Product
{
/// <summary>
/// 主键Id
/// </summary>
public int Id { get; set; }
/// <summary>
/// 产品名称
/// </summary>
public string ProductName { get; set; }
/// <summary>
/// 市场价
/// </summary>
public decimal MarketPrice { get; set; }
/// <summary>
/// 售价
/// </summary>
public decimal SellingPrice { get; set; }
/// <summary>
/// 类别Id
/// </summary>
public int CategoryId { get; set; }
/// <summary>
/// 简介
/// </summary>
public string Introduction { get; set; }
/// <summary>
/// 是否上架
/// </summary>
public bool IsOnSale { get; set; }
/// <summary>
/// 添加时间
/// </summary>
public DateTime AddTime { get; set; }
/// <summary>
/// 产品类别
/// </summary>
public class ProductCategory
{
/// <summary>
/// 主键Id
/// </summary>
public int Id { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
}
2、在项目中添加类库DAL
DAL中添加数据访问助手类:DBHelper类
public class DBHelper
{
//1.声明一个数据库连接字符串
private static readonly string connStr = ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString;
/// <summary>
/// 执行增删改的方法,返回受影响的行数
/// </summary>
/// <param name="sql"></param>
/// <param name="pams"></param>
/// <returns></returns>
public static int ExecuteNonQuery(string sql, params SqlParameter[] pams)
{
//1.声明数据库连接
using (SqlConnection conn = new SqlConnection(connStr))
{
//2.声明命令
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
//添加参数
if (pams != null && pams.Length > 0)
{
cmd.Parameters.AddRange(pams);
}
//3.打开数据库连接
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
//4.执行命令
return cmd.ExecuteNonQuery();
}
}
}
/// <summary>
/// 执行查询,返回首行首列
/// </summary>
/// <param name="sql"></param>
/// <param name="pams"></param>
/// <returns></returns>
public static object ExecuteScalar(string sql, params SqlParameter[] pams)
{
//1.声明数据库连接
using (SqlConnection conn = new SqlConnection(connStr))
{
//2.声明命令
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
//添加参数
if (pams != null && pams.Length > 0)
{
cmd.Parameters.AddRange(pams);
}
//3.打开数据库连接
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
//4.执行命令
return cmd.ExecuteScalar();
}
}
}
/// <summary>
/// 执行查询,返回SqlDataReader对象
/// </summary>
/// <param name="sql"></param>
/// <param name="pams"></param>
/// <returns></returns>
public static SqlDataReader ExecuteReader(string sql, params SqlParameter[] pams)
{
//1.声明数据库连接
SqlConnection conn = new SqlConnection(connStr);
//2.声明命令
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
//添加参数
if (pams != null && pams.Length > 0)
{
cmd.Parameters.AddRange(pams);
}
//3.打开数据库连接
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
//4.执行命令
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
}
/// <summary>
/// 执行查询,返回DataTable对象
/// </summary>
/// <param name="sql"></param>
/// <param name="pams"></param>
/// <returns></returns>
public static DataTable ExecuteTable(string sql, params SqlParameter[] pams)
{
//1.声明SqlDataAdapter对象
using (SqlDataAdapter sda = new SqlDataAdapter(sql, connStr))
{
//2.添加参数
if (pams != null && pams.Length > 0)
{
sda.SelectCommand.Parameters.AddRange(pams);
}
DataTable dt = new DataTable();
sda.Fill(dt);
return dt;
}
}
/// <summary>
/// 执行查询,返回SqlDataReader对象(存储过程)
/// </summary>
/// <param name="sql"></param>
/// <param name="pams"></param>
/// <returns></returns>
public static SqlDataReader ExecuteReaderProc(string procName, params SqlParameter[] pams)
{
//1.声明数据库连接
SqlConnection conn = new SqlConnection(connStr);
//2.声明命令
using (SqlCommand cmd = new SqlCommand(procName, conn))
{
//指定查询命令的存储过程类型
cmd.CommandType = CommandType.StoredProcedure;
//添加参数
if (pams != null && pams.Length > 0)
{
cmd.Parameters.AddRange(pams);
}
//3.打开数据库连接
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
//4.执行命令
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
}
/// <summary>
/// 执行增删改的方法,返回受影响的行数(存储过程版)
/// </summary>
/// <param name="sql"></param>
/// <param name="pams"></param>
/// <returns></returns>
public static int ExecuteNonQueryProc(string procName, params SqlParameter[] pams)
{
//1.声明数据库连接
using (SqlConnection conn = new SqlConnection(connStr))
{
//2.声明命令
using (SqlCommand cmd = new SqlCommand(procName, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
//添加参数
if (pams != null && pams.Length > 0)
{
cmd.Parameters.AddRange(pams);
}
//3.打开数据库连接
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
//4.执行命令
return cmd.ExecuteNonQuery();
}
}
}
}
DBHelper
DAL中添加ProductDAL类和ProductCategoryDAL,并在该类中添加实现增删改查的方法:
ProductCategoryDAL中获取类别信息:
/// <summary>
/// 获取产品类别
/// </summary>
/// <returns></returns>
public DataTable GetCategoryList()
{
//声明SQL语句
string sql = "select * from ProductCategory";
return DbHelper.ExecuteTable(sql);
}
ProductDAL中获取产品信息:
/// <summary>
/// 查询产品列表
/// </summary>
/// <param name="productName"></param>
/// <param name="categoryId"></param>
/// <returns></returns>
public DataTable GetProductList(string productName,int categoryId)
{
//定义SQL语句
string sql = "select p.Id,p.ProductName,p.MarketPrice,p.SellingPrice,pc.Name,p.IsOnSale,p.AddTime from Product as p left join ProductCategory as pc on p.CategoryId=pc.Id";
List<string> listWhere = new List<string>();
List<SqlParameter> listPams = new List<SqlParameter>();
//多条件判断
if (!string.IsNullOrWhiteSpace(productName))
{
listWhere.Add("p.ProductName like @ProductName");
listPams.Add(new SqlParameter("@ProductName", $"%{productName}%"));
}
if (categoryId > 0)
{
listWhere.Add("p.CategoryId = @CategoryId");
listPams.Add(new SqlParameter("@CategoryId", categoryId));
}
if (listWhere.Count > 0)
{
sql += " where " + string.Join(" and ", listWhere.ToArray());
}
return DbHelper.ExecuteTable(sql, listPams.ToArray());
}
查询产品列表
/// <summary>
/// 根据Id获取产品
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public Product GetProductById(int id)
{
string sql = "select * from Product where Id=@Id";
SqlParameter[] pams = {
new SqlParameter("@Id",id)
};
SqlDataReader reader = DbHelper.ExecuteReader(sql, pams);
if(reader.HasRows)
{
if(reader.Read())
{
Product model = new Product() {
AddTime=Convert.ToDateTime(reader["AddTime"]),
CategoryId=Convert.ToInt32(reader["CategoryId"]),
Id=Convert.ToInt32(reader["Id"]),
Introduction=reader["Introduction"].ToString(),
IsOnSale=Convert.ToBoolean(reader["IsOnSale"]),
MarketPrice=Convert.ToDecimal(reader["MarketPrice"]),
ProductName=reader["ProductName"].ToString(),
SellingPrice=Convert.ToDecimal(reader["SellingPrice"])
};
return model;
}
}
return null;
}
根据Id获取产品
/// <summary>
/// 更新产品
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public bool Update(Product model)
{
string sql = "update Product set ProductName=@ProductName,MarketPrice=@MarketPrice,SellingPrice=@SellingPrice,CategoryId=@CategoryId, Introduction=@Introduction,IsOnSale=@IsOnSale where Id=@Id";
SqlParameter[] pams = {
new SqlParameter("@ProductName",model.ProductName),
new SqlParameter("@MarketPrice",model.MarketPrice),
new SqlParameter("@SellingPrice",model.SellingPrice),
new SqlParameter("@CategoryId",model.CategoryId),
new SqlParameter("@Introduction",model.Introduction),
new SqlParameter("@IsOnSale",model.IsOnSale),
new SqlParameter("@Id",model.Id)
};
return DbHelper.ExecuteNonQuery(sql, pams) > 0;
}
更新产品
/// <summary>
/// 删除产品
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public bool Delete(int id)
{
string sql = "delete from Product where Id=@Id";
SqlParameter[] pams = {
new SqlParameter("@Id",id)
};
return DbHelper.ExecuteNonQuery(sql, pams) > 0;
}
删除产品
/// <summary>
/// 添加产品
/// </summary>
/// <param name="model">产品实体</param>
/// <returns></returns>
public bool Add(Product model)
{
string sql = "insert into Product values(@ProductName,@MarketPrice,@SellingPrice,@CategoryId,@Introduction,@IsOnSale,@AddTime)";
SqlParameter[] pams = {
new SqlParameter("@ProductName",model.ProductName),
new SqlParameter("@MarketPrice",model.MarketPrice),
new SqlParameter("@SellingPrice",model.SellingPrice),
new SqlParameter("@CategoryId",model.CategoryId),
new SqlParameter("@Introduction",model.Introduction),
new SqlParameter("@IsOnSale",model.IsOnSale),
new SqlParameter("@AddTime",DateTime.Now)
};
return DbHelper.ExecuteNonQuery(sql, pams) > 0;
}
添加产品
3、在项目中添加BLL类库
BLL中添加ProductBLL和ProductCategoryBLL类,并添加业务逻辑层方法:
public class ProductCategoryBLL
{
ProductCategoryDAL dal = new ProductCategoryDAL();
/// <summary>
/// 获取产品类别
/// </summary>
/// <returns></returns>
public DataTable GetCategoryList()
{
return dal.GetCategoryList();
}
}
public class ProductBLL
{
ProductDAL dal = new ProductDAL();
/// <summary>
/// 查询产品列表
/// </summary>
/// <param name="productName"></param>
/// <param name="categoryId"></param>
/// <returns></returns>
public DataTable GetProductList(string productName, int categoryId)
{
return dal.GetProductList(productName, categoryId);
}
/// <summary>
/// 获取Id获取产品
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public Product GetProductById(int id)
{
return dal.GetProductById(id);
}
/// <summary>
/// 更新产品
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public bool Update(Product model)
{
return dal.Update(model);
}
/// <summary>
/// 删除产品
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public bool Delete(int id)
{
return dal.Delete(id);
}
/// <summary>
/// 添加产品
/// </summary>
/// <param name="model">产品模型</param>
/// <returns></returns>
public bool Add(Product model)
{
return dal.Add(model);
}
}
4、在项目中添加Asp.NET Web应用程序,作为UI层,并添加Web窗体
在窗体中添加GridView控件,并进行数据绑定的配置:
界面效果如下:

在后台调用BLL层中获取产品类别的方法,并将数据绑定到下拉框中:
ProductBLL bllProduct = new ProductBLL();
ProductCategoryBLL bllCategory = new ProductCategoryBLL();
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
//绑定类别下拉框
BindCategory();
//绑定产品
BindProductList();
}
}
/// <summary>
/// 绑定类别下拉框方法
/// </summary>
private void BindCategory()
{
DataTable dt = bllCategory.GetCategoryList();
ddl_Category.DataSource = dt;
ddl_Category.DataTextField = "Name";
ddl_Category.DataValueField = "Id";
ddl_Category.DataBind();
//插入默认项
ddl_Category.Items.Insert(0, new ListItem("全部", "0"));
}
调用BLL层中获取产品信息的业务逻辑方法,将数据绑定到GridView中:
/// <summary>
/// 绑定产品方法
/// </summary>
private void BindProductList()
{
string productName = txt_ProductName.Text.Trim();
int categoryId = Convert.ToInt32(ddl_Category.SelectedValue);
DataTable dt = bllProduct.GetProductList(productName, categoryId);
gv_Product.DataSource = dt;
gv_Product.DataBind();
}
为查询按钮添加一个点击事件,进行多条件查询:
/// <summary>
/// 多条件搜索
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btn_Search_Click(object sender, EventArgs e)
{
BindProductList();
}
至此,列表查询功能完成
-----------------------------------------------------------
在GridView中进行删除按钮的处理
先在操作模板列上添加一个LinkButton服务器控件,并添加CommandName和CommandArgument这两个属性:

添加一个js确认提示:

在GirdView中添加RowCommand事件:

调用BLL层中的删除产品的方法:
protected void gv_Product_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "del")
{
int id = Convert.ToInt32(e.CommandArgument);
if (bllProduct.Delete(id))
{
ClientScript.RegisterStartupScript(this.GetType(), "alert", "<script>alert('删除成功')</script>");
BindProductList();
}
else
{
ClientScript.RegisterStartupScript(this.GetType(), "alert", "<script>alert('删除失败')</script>");
}
}
}
---------------------------------------------------------
添加产品功能实现:
按照要求在页面中添加控件:

在后台页面加载的时读取下拉框数据:
ProductBLL bllProduct = new ProductBLL();
ProductCategoryBLL bllCategory = new ProductCategoryBLL();
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
//绑定类别下拉框
BindCategory();
}
}
/// <summary>
/// 绑定类别下拉框方法
/// </summary>
private void BindCategory()
{
DataTable dt = bllCategory.GetCategoryList();
ddl_Category.DataSource = dt;
ddl_Category.DataTextField = "Name";
ddl_Category.DataValueField = "Id";
ddl_Category.DataBind();
}
绑定下拉菜单
为按钮添加点击事件实现添加功能:
protected void btn_Save_Click(object sender, EventArgs e)
{
Product model = new Product()
{
CategoryId = Convert.ToInt32(ddl_Category.SelectedValue),
Introduction = txt_Introduction.Text,
IsOnSale = ck_IsOnSale.Checked,
MarketPrice = Convert.ToDecimal(txt_MarketPrice.Text),
ProductName = txt_ProductName.Text,
SellingPrice = Convert.ToDecimal(txt_SellingPrice.Text)
};
//添加成功后,弹出消息提示,并跳转至首页
if (bllProduct.Add(model))
{
Page.ClientScript.RegisterStartupScript(this.GetType(), "alert", "<script>alert('添加成功!');location.href='/Index.aspx';</script>");
}
else
{
Page.ClientScript.RegisterStartupScript(this.GetType(), "alert", "<script>alert('添加失败!');</script>");
}
}
--------------------------------------------------
编辑产品功能实现:
按照要求在页面中添加控件:

在类别页面添加跳转到编辑页面的链接:

在后台添加获取下拉框数据的方法:
ProductBLL bllProduct = new ProductBLL();
ProductCategoryBLL bllCategory = new ProductCategoryBLL();
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
//绑定类别下拉框
BindCategory();
//绑定产品信息
BindProductInfo();
}
}
/// <summary>
/// 绑定类别下拉框方法
/// </summary>
private void BindCategory()
{
DataTable dt = bllCategory.GetCategoryList();
ddl_Category.DataSource = dt;
ddl_Category.DataTextField = "Name";
ddl_Category.DataValueField = "Id";
ddl_Category.DataBind();
}
绑定下拉菜单
添加根据id获取当前编辑的产品的方法:
/// <summary>
/// 绑定产品信息方法
/// </summary>
private void BindProductInfo()
{
int id = Convert.ToInt32(Request.QueryString["id"]);
Product model = bllProduct.GetProductById(id);
if (model != null)
{
txt_ProductName.Text = model.ProductName;
txt_Introduction.Text = model.Introduction;
txt_MarketPrice.Text = model.MarketPrice.ToString();
txt_SellingPrice.Text = model.SellingPrice.ToString();
ddl_Category.SelectedValue = model.CategoryId.ToString();
ck_IsOnSale.Checked = model.IsOnSale;
lbl_Id.Text = model.Id.ToString();
}
}
为按钮添加点击事件实现更新:
/// <summary>
/// 提交修改
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btn_Save_Click(object sender, EventArgs e)
{
Product model = new Product()
{
CategoryId = Convert.ToInt32(ddl_Category.SelectedValue),
Id = Convert.ToInt32(Request.QueryString["id"]),
Introduction = txt_Introduction.Text,
IsOnSale = ck_IsOnSale.Checked,
MarketPrice = Convert.ToDecimal(txt_MarketPrice.Text),
ProductName = txt_ProductName.Text,
SellingPrice = Convert.ToDecimal(txt_SellingPrice.Text)
};
//修改成功后,弹出消息提示,并跳转至首页
if (bllProduct.Update(model))
{
Page.ClientScript.RegisterStartupScript(this.GetType(), "alert", "<script>alert('修改成功!');location.href='/Index.aspx';</script>");
}
else
{
Page.ClientScript.RegisterStartupScript(this.GetType(), "alert", "<script>alert('修改失败!');</script>");
}
}
-----------------------------
利用三层架构与GridView相结合,GirdView的使用方法和之前基本一致,改变为三层架构之后,我们发现原本在UI层中的代码明显减少了很多,通过ADO.NET访问数据库的部分放在了数据访问层,体现出了高内聚、低耦合的思想。