(八)Asp.NET中三层架构的应用

一、 什么是三层架构?

生活中的三层

初始结构:

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

 

 改变成三层:

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

 

使用三层架构的目的:

目的:解耦

服务员(UI层)请假——另找服务员

厨师(BLL层)辞职——招聘另一个厨师

采购员(DAL层)辞职——招聘另一个采购员

顾客反映:

你们店服务态度不好——服务员的问题。开除服务员

你们店菜里有虫子——厨师的问题。换厨师

UI(表现层):主要是指与用户交互的界面。用于接收用户输入的数据和显示处理后用户需要的数据。

BLL(业务逻辑层):UI层和DAL层之间的桥梁。实现业务逻辑。业务逻辑具体包含:验证、计算、业务规则等等。

DAL(数据访问层):与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交给业务层,同时将业务层处理的数据保存到数据库。(当然这些操作都是基于UI层的。用户的需求反映给界面(UI),UI反映给BLLBLL反映给DALDAL进行数据的操作,操作后再一一返回,直到将用户所需数据反馈给用户)

那么在三层架构中各层之间为了能够处理一个统一的功能,相互直接是如何依赖的呢?

我们从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中添加ProductBLLProductCategoryBLL类,并添加业务逻辑层方法:

 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访问数据库的部分放在了数据访问层,体现出了高内聚、低耦合的思想。

原文地址:https://www.cnblogs.com/JuneDream/p/14077650.html