三层架构是一种常用的软件开发架构模式,它将应用程序分为三个层次:表示层、业务逻辑层和数据访问层。每一层都有明确的职责和功能,分别负责用户交互、业务处理和数据存储等任务。这种架构模式的优点包括易于维护和扩展、更好的组织结构和代码重用性、更高的安全性等方面。在具体实现中,需要根据不同的应用程序需求和技术平台选择适当的工具和框架,以达到最佳的开发效果和应用程序质量。

 

在 .NET 中,我们同样可以使用三层架构来构建我们的应用程序,实现表示层、业务逻辑层和数据访问层的分离。下面分别介绍在 .NET 中如何实现这三层架构。

        1.表示层

在 .NET 中,我们可以使用 WinForms、WPF、ASP.NET 等技术来实现表示层的功能。在 WinForms 和 WPF 中,我们可以使用各种控件来构建用户界面,以显示和收集数据。在 ASP.NET 中,我们可以使用 ASP.NET Web 窗体、MVC、Web API 等技术来生成和管理 Web 页面。

在所有情况下,表示层的主要职责是接收用户输入和显示数据。例如,在 WinForms 中,我们可以使用窗体和控件来表示 UI,并通过事件处理程序和数据绑定来响应用户输入和显示数据。在 ASP.NET 中,我们可以使用代码片段来生成 HTML、CSS 和 JavaScript,并使用服务器端脚本和控件来处理事件和数据绑定。

        2.业务逻辑层

在 .NET 中,我们可以使用类和接口来实现业务逻辑层。这些类和接口通常包括与应用程序相关的业务逻辑和规则,例如验证、授权、计算和处理数据。

业务逻辑层通常向表示层公开 API,以便 UI 可以与业务逻辑进行交互。例如,在 ASP.NET MVC 中,我们可以使用控制器和模型来处理请求,并使用视图来处理 UI。在 WinForms 中,我们通常将业务逻辑封装在一个专用的类或类库中,并使用事件处理程序和数据绑定来处理 UI。

        3.数据访问层

在 .NET 中,我们可以使用 ADO.NET 和 Entity Framework 来访问数据。ADO.NET 提供了一组用于连接到和操作关系型数据库的类和接口,而 Entity Framework 则提供了一种对象关系映射(ORM)框架,使我们可以将对象与数据库记录进行映射和交互。

数据访问层通常会使用一些技术和工具来管理和维护数据,例如 SQL Server Management Studio 或 LINQ。我们可以使用 ADO.NET 和 Entity Framework 来执行数据库操作,并使用仓储模式或 DAL 等设计模式来创建底层数据库连接和访问代码。

 下面通过一个简易的例题来演示三层架构的简易模式:

  1. 首先建立一个数据库(EmployeeSystem)
  2. 内部包含两个表(AdminInfo),(EmployeeInfo)
  3. AdminInfo表(登录验证表)包含两字段:主键:Account(varchar)用户名,Pwd(varchar) 密码  用于登录验证
  4. EmployeeInfo表(学生信息表)包含六个字段 :主键自增列:Id(int)编号,EmployeeName (varchar)  姓名,EmployeeNo(varchar)学号,EmployeeAge(int )年龄,EmployeeSalary(int)就业工资,EmployeeJob(varchar)职业名称

  以上完成了示例数据库的搭建 

接下来打开Microsoft Visual Studio2022,开始新建项目

 

  1.  在解决方案里创建一个Windows窗体应用程序取名UI作为我们这个项目的表示层
  2. 在解决方案里添加三个类库
  3. Model 模型层主要包含一些数据库中表对应的实体类,作为数据传递的载体
  4. DAL  数据访问层 :与数据库进行交互 执行增删改查的操作
  5. BLL 业务逻辑层:对业务逻辑进行处理 ,比如进行判断(必要的非空判断,添加前是否已经存在同样的数据)

 

        6.打开UI找到引用:

 第2步:定义连接字符串

        name 属性是用于标识连接字符串的名称的字符串。

        connectionString 是数据库连接时用到的字符串。

         providerName 属性是用于指定数据提供程序的名称字符串。它通常与 connectionString 属性一起使用,来告诉应用程序要使用哪个特定的 ADO.NET 数据提供程序来访问数据库。

最后再到UI层

 

 到这里了接下来进入Model层

 我们首先从模型层开始写:

这里类里添加了两个类分别对应数据库里的两张表以及各字段 

到这个模型层算搭建完(记得把修饰符改成公有的)

 下一步给各层设置相互之间的引用

  1.  UI表示层需要引用BLL业务逻辑层和Model模型层
  2. DAL数据访问层需要引入Model模型层
  3. BLL业务逻辑层需要引入DAL数据访问层和Model模型层

 到这个项目的引用关系也就完事了

接下来就可以开始着手DAL数据访问层的内容了

DAL 层中的 SqlHelper 方法通常被称为数据访问助手方法(Data Access Helper Method),它们主要用于将应用程序的业务逻辑和底层数据访问逻辑分离开来。

通常情况下,SqlHelper 方法都包装了底层 ADO.NET API,以提供更简单、更易用的 API 接口,从而帮助开发人员更轻松地访问数据库。这些方法通常包括执行 SQL 语句、存储过程的方法,还有获取数据表、数据集等返回值的方法。

SqlHelper 方法可以接受连接字符串、SQL 语句、存储过程名称以及参数等信息作为参数,并使用它们来构建和执行数据库操作。通过将这些详细信息隐藏在方法内部,SqlHelper 方法帮助程序员简化了代码逻辑,提高了代码结构的清晰度和可读性。同时,SqlHelper 方法还可以处理数据库连接、异常处理等底层逻辑,从而提高了数据访问代码的可靠性和稳定性。

需要注意的是,不同的数据访问技术和开发框架通常有不同的 SqlHelper 实现方式,例如在 ASP.NET 中,我们通常使用 SqlHelper 类来访问 SQL Server 数据库,而在 Entity Framework 中,我们可以使用 DbContext 来管理数据访问逻辑,或者使用 LINQ 表达式来查询数据。

  1.  建立一个公有,静态类SqlHelper (内部方法也均为公有静态方法)
static readonly string ido= ConfigurationManager.ConnectionStrings["配置文件内add里的name名"].ToString();
//这段代码的作用是从应用程序配置文件 (App.config) 中获取名为 “配置文件内add里的name名” 的连接字符串,并将其存储在一个名为 “ido” 的静态只读字符串常量 ido 中。

     2.接下来写下两个方法

        思路:根据执行数据库增删改查操作的返回值来分类方法

                        1.查询操作返回的是DataTable 是一个 C# 中的数据表格类,它代表了一个内存中的数据表格,提供了一种方便的方法来存储和操作数据。

                        2.增删改操作的不同点只是sql语句有差异,他们共同点的返回类型都是受影响的行数。

                        3.由此存在了两种返回类型的值,可以在拟定两个返回值类型的方法分别为int和DataTable

   3.查询表值的方法返回DAtaTable

public static DataTable selectall(string sql)
        {
            //建立数据库连接 App.config里准备的数据库连接语句在这个时候可以体现用处了
            SqlConnection conn = new SqlConnection(ido);
            //打开数据库连接
            conn.Open();
            // 创建数据适配器对象
            SqlDataAdapter ter = new SqlDataAdapter(sql, conn);
            //实例化DataTable
            DataTable dt = new DataTable();
            // 使用数据适配器填充数据集
            ter.Fill(dt);
            //关闭数据库连接
            conn.Close();
            //返回值
            return dt;
        }

   4.增删改的方法返回受影响行数

public static int CUD(string sql)
        {
            SqlConnection conn = new SqlConnection(ido);
            conn.Open();
            //创建sql命令对象
            SqlCommand com= new SqlCommand(sql,conn);
            //用一个int 变量来接收受影响的返回行数
            int r=com.ExecuteNonQuery();
            //返回r的值
            return r;
        }

   5.参数化命令的增删改方法

public static bool selectal(string sql,List<SqlParameter> list)
        {
            
            SqlConnection conn = new SqlConnection(ido);
            conn.Open();
            SqlCommand com = new SqlCommand(sql, conn);
            //使用 AddRange 方法一次性添加多个 SqlParameter 对象,list需要转换成数组添加
            com.Parameters.AddRange(list.ToArray());
            //接收执行查询时返回的结果集中第一行第一列的值如果有数据就会大于0
            int r = Convert.ToInt32(com.ExecuteScalar());
            //判断r的值来决定返回的bool
            if(r >0) { return true; } else { return false; }
        }

到这一步数据访问助手的类已经完成了

DAL 数据访问层再为每张表定义一个类调用数据访问助手(在这里可以写入你需要执行的sql语句操作)

首先我们来做最简单的AdminInfo表(登录验证表)做执行一个登录的查询操作

        这里需要主要预防sql注入攻击,所以要采用参数化命令的方法

 在DAL层创建一个AdmDAL类调用SqlHelper类的方法

//参数为AdminInfo类
        public bool admopen(AdminInfo info)
        {
            //sql语句
            string sql = "select count(*) from AdminInfo where Account=@Account and Pwd=@Pwd";
            //创建了一个 List<SqlParameter> 实例,用于存储多个 SqlParameter 对象。然后通过调用 List<T> 对象的 Add 方法向集合添加一个参数,或使用 AddRange 方法一次添加多个参数
            List<SqlParameter> list= new List<SqlParameter>();
            list.Add(new SqlParameter("@Account", info.Account));
            list.Add(new SqlParameter("@Pwd", info.Pwd));
            //返回值调用SqlHelper的方法(传入sql语句和参数化命令对象的值)最终返回一个bool类型的值
            return SqlHelper.selectal(sql, list);
            

        }

DAL层最后一个类EmpADL类 EmployeeInfo表(学生信息表)的各项操作

全部采取的sqlHelper的方法

简易的只需要写一条sql语句再返回调用方法(传入string sql语句参数)

有参数化命令的方法就根据sql语句创建一个list集合来存储方法类的值再返回参数化命令方法(sql语句,list集合)

 //添加学生的方法
        public int EmpInsert(EmployeeInfo info)
        {
            string sql = "insert into EmployeeInfo values(@EmployeeName,@EmployeeNo,@EmployeeAge,@EmployeeSalary,@EmployeeJob)";
            List<SqlParameter> list = new List<SqlParameter>();
            list.Add(new SqlParameter("@EmployeeName", info.EmployeeName));
            list.Add(new SqlParameter("@EmployeeNo", info.EmployeeNo));
            list.Add(new SqlParameter("@EmployeeAge", info.EmployeeAge));
            list.Add(new SqlParameter("@EmployeeSalary", info.EmployeeSalary));
            list.Add(new SqlParameter("@EmployeeJob", info.EmployeeJob));
            return SqlHelper.CUD(sql, list);
        }
        //修改方法
        public int EmpUpdate(EmployeeInfo info)
        {
            string sql = "update EmployeeInfo set EmployeeName=@EmployeeName,EmployeeNo=@EmployeeNo,EmployeeAge=@EmployeeAge,EmployeeSalary=@EmployeeSalary,EmployeeJob=@EmployeeJob where Id=@Id";
            List<SqlParameter> list = new List<SqlParameter>();
            list.Add(new SqlParameter("@EmployeeName", info.EmployeeName));
            list.Add(new SqlParameter("@EmployeeNo", info.EmployeeNo));
            list.Add(new SqlParameter("@EmployeeAge", info.EmployeeAge));
            list.Add(new SqlParameter("@EmployeeSalary", info.EmployeeSalary));
            list.Add(new SqlParameter("@EmployeeJob", info.EmployeeJob));
            list.Add(new SqlParameter("@Id", info.Id));
            return SqlHelper.CUD(sql, list);
        }
        //删除方法
        public int EmpDelete(int id)
        {
            string sql = $"delete from EmployeeInfo where Id={id}";
            return SqlHelper.CUD(sql);
        }
        //查询整表的方法
        public DataTable select()
        {
            string sql = "select * from EmployeeInfo";
            return SqlHelper.selectall(sql);
        }
        //根据编号查询的方法
        public DataTable selectID(int id) {
            string sql = $"select * from EmployeeInfo where Id={id}";
            return SqlHelper.selectall(sql);
        }
        //根据姓名模糊查询的方法
        public DataTable SelectName(string name) {

            string sql = $"select * from EmployeeInfo where EmployeeName like '%{name}%'";
            return SqlHelper.selectall(sql);
        }

以上这些就是DAL 数据访问层的基本方法 如果有额外的需求的话可以根据个人的要求结合所学的知识再去新建方法体

下面我们再来到BLL业务逻辑层

业务逻辑层需要做什么呢?

简单的来讲就是连接UI表示层和DAL数据访问层的桥梁,在这里会对数据进行一些加工例如判断数据是否为空等等,该怎么做需要屏幕前的你来发挥了

下面来看示例

 public class EmpBLL
    {
        //实例化DAL数据访问层的EmpDAL类 并用方法调用EMpDAL类的方法
        private EmpDAL EmpDAL = new EmpDAL();
        //添加的方法
        public int EmpInsert(EmployeeInfo info)
        {
            return EmpDAL.EmpInsert(info);
        }
        //修改的方法
        public int EmpUpdate(EmployeeInfo info)
        {
            return EmpDAL.EmpUpdate(info);
        }
        //删除的方法
        public int EmpDelete(int id)
        {
            return EmpDAL.EmpDelete(id);
        }
        //查询整表的方法
        public DataTable select()
        {
            return EmpDAL.select();
        }
        //根据姓名模糊查询的方法
        public DataTable SelectName(string name)
        {
            return EmpDAL.SelectName(name);
        }
        //根据编号查询的方法
        public DataTable SelectID(int id)
        {
            return EmpDAL.selectID(id);
        }

以上是EmployeeInfo学生信息表需要在表示层使用的方法

接下来是AdminInfo登录信息表需要的业务逻辑层方法

 public class AdmBLL
    {
        //实例化数据访问层的AdmDAL类 并调用方法
        private AdmDAL AdmDAL=new AdmDAL();
        //登录方法
        public bool admopen(AdminInfo info)
        {
            return AdmDAL.admopen(info);
        }

    }

到这里业务逻辑层也就完工了

最后一个UI 表示层 使用WinForms窗体应用程序搭建

首先完成一个简易的登录操作

在UI层建立一个窗体form1 

使用控件 label* 2 ,textbox*2 button*1

 给窗体登录按钮 双击添加点击事件

private void button1_Click(object sender, EventArgs e)
        {
            //实例化模型层Model 的AdminInfo类 并把文本框的值赋给里面的字段
            AdminInfo info= new AdminInfo();
            info.Account = textBox1.Text;
            info.Pwd= textBox2.Text;
            //实例化BLL 业务逻辑层调用相对应的方法
            AdmBLL adm= new AdmBLL();
            //定义一个bool值的变量来接收返回值
            bool f= adm.admopen(info);
            //通过判断f的值来选择执行的操作,为true则弹窗登录成功并打开后续的窗体
            if (f==true)
            {
                MessageBox.Show("登录成功");
                Form2 form2 = new Form2();
                form2.Show();
            }
            else { MessageBox.Show("登录失败"); }
        }

最后再给UI表示层添加一个form2窗体用于示例DAL和BLL的方法

为了简易采用dategridview

 上面的窗体我们添加了多个按钮

先从窗体的加载事件开始,首先在窗体内部添加一个公有的BLL业务逻辑层的实例对象,便于调用其方法。

 public EmpBLL empBLL = new EmpBLL();//实例化业务逻辑层
//初始化dategridview的表值
//将方法放入form2_load窗体加载事件中即可初始化值
public void select()
        {
            dataGridView1.DataSource = empBLL.select();//调用业务逻辑层查询整表的方法加载到datagridview控件中
            dataGridView1.ClearSelection();//取消默认选中状态
        }

删除按钮:button2

//删除按钮点击事件 
private void button2_Click(object sender, EventArgs e)
        {
            //对datagridview的选中项进行判断,未选中行时弹窗提示
            if (dataGridView1.SelectedRows.Count<=0)
            {
                MessageBox.Show("请选中你需要删除的一行!");
            }
            else if (dataGridView1.SelectedRows.Count > 0)
            {
                //获取选中行的第一行第一列的值用 一个int的变量来接收方便调用后续的业务逻辑层删除的方法
                int id = Convert.ToInt32(this.dataGridView1.SelectedRows[0].Cells[0].Value);
                //接收删除方法的返回值进行受影响的行判断
                int i = empBLL.EmpDelete(id);
                if (i > 0)
                {
                    //删除成功时弹窗提示并调用给datagridview赋值的方法(select())
                    MessageBox.Show("删除成功");
                    select();
                }
                else { MessageBox.Show("删除失败"); }

            }
            
        }

添加的按钮:button1

private void button1_Click(object sender, EventArgs e)
        {
            //设置跳转到目标窗体并设置父子级
            Form3 form3 = new Form3();
            form3.Owner= this;
            form3.Show();
        }

form3窗体如下:

注:编号Id为自增列,所有只有五个字段

 这里只需要为添加按钮添加点击事件:

//添加按钮点击事件 
private void button1_Click(object sender, EventArgs e)
        {
            //实例化EmployeeInfo类
            EmployeeInfo info = new EmployeeInfo();
            info.EmployeeName=textBox1.Text;
            info.EmployeeNo=textBox2.Text;
            info.EmployeeAge=int.Parse(textBox3.Text);
            info.EmployeeSalary=int.Parse(textBox4.Text);
            info.EmployeeJob=textBox5.Text;
            //给类中的每个字段赋值
            EmpBLL empBLL = new EmpBLL();
            //实例化BLL业务逻辑层的EmpBLL方法类,调用其中的添加方法把info作为参数传入
            int r= empBLL.EmpInsert(info);
            //用变量接收返回值来判断受影响行数
            if (r > 0) { 
                MessageBox.Show("添加成功"); 
                Form2 form=this.Owner as Form2;
                form.select();
                this.Close();
                
            
            } else { MessageBox.Show("添加失败"); }
        }

Form2 form=this.Owner as Form2:

这段的理解是:将当前窗体的拥有者窗体转换为 Form2 类型,并将转换后的实例赋值给了 form 变量。这样,我们就可以直接访问该窗体对象的属性和方法,例如 form 属性或者 Show 方法等。需要注意的是,如果 this.Owner 不是 Form2 类型的对象,则 form 变量将为 null。

之后再调用form2窗体的select()方法实现添加成功之后刷新form2的datagridview的集合

剩余的几个按钮就不一一进行讲解了,原理同上,有时间可以自己多练习熟能生巧,该文章只供参考用于理解软件开发模式的三层架构,相信认真看了的小伙伴能够看出对于传统的一个控件一个控件来完善的方式节省的代码量是非常可观的。

最后文章到这里就结束了,如果感觉对你有帮助的话可以点赞或收藏来加深理解。

再感谢认真看完文章的人对我的认可,本人会持续更新更多对粉丝有用的文章喜欢的可以关注支持哦

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐