零基础学C#3.0 -- .net的三层架构

新手学C# ——.net的三层架构之最简单实例:登录界面

 

三层架构包括:

1. 数据访问层(Data Access Layer, DAL):负责将底层数据传送到业务逻辑层

2. 业务逻辑层(Business Logic Layer, BLL):处理数据访问层传送的数据,并实现业务逻辑

3. 表示层(User Interface, UI):不处理任何业务,负责显示与实时更新

其中1--2--3层次依次上升。

为了使信息能够顺利由底层传送,可以采用实体类的方法,添加实体层。实体类通常与数据库中字段相互对应,拥有get和set属性。实体类的目的是用于替代DataSet,通过类的方式传递数据,并使逻辑更加清晰。

三层架构来源于MVC,其核心思想是保持三层之间的相互独立,在对其他功能进行局部修改时,可以使三层结构保持大体不变,以免进行全局的修改。换句话说,在修改某局部代码后,你自然而然的知道何处的代码应该进行修改何处的代码可以不进行修改如何规范修改的局部代码对上层的接口以达到减少修改量的目的

这里不得不说的是,在三层架构中,修改局部代码对整体的变动还是很大的,层次数越多,需要修改的部分越少。当然,层次数的增加伴随着逻辑的复杂性增加和接口的复杂性增加。越是小型的项目,需要的层次数越少。

下面举一个最简单的例子:用户登录。

 

数据库中表Login的结构:

LoginID : 用户名

LoginPwd : 用户密码

 

(1)新建一个WinForm项目SchoolManager,在项目解决方案中点击右键新建三个类库(SchoolManager.DAL, SchoolManager.BLL, SchoolManager.Model),分别表示数据访问层、业务逻辑层和实体层。

(2)右键单击类库,添加引用(BLL引用DAL,UI引用BLL,三者都引用Model),表达四者之间的逻辑依赖关系。这里强调的是,UI不引用DAL。

(3)编写实体类Login.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SchoolManager.Model
{
    public class Login
    {
        private int stuID;  //学号
        public int StuID
        {
            get { return stuID; }
            set { stuID = value; }
        }

        private string studentName;  //姓名
        public string StudentName
        {
            get { return studentName; }
            set { studentName = value; }
        }

        private string loginID;  //登录名
        public string LoginID
        {
            get { return loginID; }
            set { loginID = value; }
        }

        private string loginPwd;  //登录密码
        public string LoginPwd 
        {
            get { return loginPwd; }
            set { loginPwd = value; }
        }

        private int state;
        public int State 
        {
            get { return state; }
            set { state = value; }
        }

    }
}

这个实体类与数据库中的表一一对应(这里没有对应的原因是实际程序中只用到了两个字段,实际数据库中的表以此类中的字段为准),也就是说,数据库中表的每一行都可以转化为这个类中的一个实例,从而在各个层中传递数据。

(4)编写DAL类LoginService.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SchoolManager.Model;
using System.Data.SqlClient;

namespace SchoolManager.DAL
{
    public static class LoginService
    {
        static string connStr = @"Data Source=(local)\SQLEXPRESS;Initial Catalog=StudentInfo;Integrated Security=True;Pooling=False";
        private static SqlConnection sqlConn = new SqlConnection(connStr);

        /// <summary>
        /// 根据登录名返回用户类
        /// </summary>
        /// <param name="loginID">登录名</param>
        /// <returns>用户类</returns>
        public static Login GetLoginByLoginID(string loginID)
        {
            string sql = "select * from Login where LoginID=‘" + loginID + "";
            try
            {
                SqlCommand sqlCmd = new SqlCommand(sql, sqlConn);
                sqlConn.Open();
                SqlDataReader sqlReader = sqlCmd.ExecuteReader();
                if (sqlReader.Read())
                {
                    Login user = new Login();
                    user.LoginID = Convert.ToString(sqlReader["LoginID"]);
                    user.LoginPwd = Convert.ToString(sqlReader["LoginPwd"]);
                    user.State = Convert.ToInt32(sqlReader["State"]);
                    user.StudentName = Convert.ToString(sqlReader["StudentName"]);
                    user.StuID = Convert.ToInt32(sqlReader["StuID"]);
                    sqlReader.Close();
                    sqlConn.Close();
                    return user;
                }
                else
                {
                    sqlConn.Close();
                    sqlReader.Close();
                    return null;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw ex;
            }
        }
    }
}

这里注意几点:

a)此类(以及之后的BLL,UI)引入了Model的命名空间,从而可以调用Model中的Login类并生成实例,这正是添加引用的原因所在。

b)此类引入了System.Data.SqlClient,也是这三层中唯一引用这个命名空间的层,即所有对数据库的操作都要在这一层的不同类中完成。

c)GetLoginByLoginIn()方法的返回值是Login类的一个实例,实质上是返回查询后的指定行数据。

(5)编写BLL类LoginManager.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SchoolManager.Model;
using SchoolManager.DAL;

namespace SchoolManager.BLL
{
    public class LoginManager
    {
        /// <summary>
        /// 从数据访问层调用数据的方法
        /// </summary>
        /// <param name="loginID">登录名</param>
        /// <returns>密码是否匹配</returns>
        public static bool GetLogin(string loginID, string loginPwd)
        {
            Login user = LoginService.GetLoginByLoginID(loginID);
            if (user.LoginPwd == loginPwd)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

    }
}

注意以下几点:

a)引用了DAL的命名空间,因为要调用DAL中的GetLoginByLoginIn()方法。

b)GetLogin()实际上已经完成了用户登录的逻辑功能,不能将判断式写入UI层中。

(6)编写UI层LoginForm.cs:(界面很简洁,用户名、密码输入框,确定、取消按钮)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SchoolManager.Model;
using SchoolManager.BLL;

namespace SchoolManager
{
    public partial class LoginForm : Form
    {
        public LoginForm()
        {
            InitializeComponent();
        }

        private void buttonLogin_Click(object sender, EventArgs e)
        {
            //检测用户名是否为空
            if(string.IsNullOrEmpty(textBoxName.Text))
            {
                MessageBox.Show("请输入用户名");
                return;
            }

            //检测密码是否为空
            if (string.IsNullOrEmpty(textBoxPwd.Text))
            {
                MessageBox.Show("请输入密码");
                return;
            }

            //调用业务逻辑层方法登录
            if(LoginManager.GetLogin(textBoxName.Text, textBoxPwd.Text))
            {
                MessageBox.Show("登陆成功");
            }
            else
            {
                MessageBox.Show("用户名或密码错误");
            }
        }

        private void buttonCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

    }
}

这里有几点需要注意:

a)引用命名空间(详见上文)

b)对TextBox中是否为空的判断可以由UI层完成,也可以在BLL层中编写Validate()函数并在UI层中调用,不过还是建议在UI层中完成,毕竟界面的修改必然伴随着Validate()方法的修改

c)UI层调用GetLogin()方法之后,只是将返回的布尔值解释为登录成功/失败,没有进行逻辑上的判断。

 

希望本文能对.net学习的菜鸟级新手有所帮助!(本人也处于这一行列)

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。