三层登录实例以及代码详解

一、实例:

现在有一个积分系统,学生每登录一次可获得10积分奖励。

这个实例只是简单的三层登录,每次登录成功就加10,不涉及其他的功能,当然,其中可能也有很多bug需要优化。

1.数据库表设计:

在这里插入图片描述

2.数据模块图:

在这里插入图片描述
细心的读者肯定会发现,除了UI,BLL,DAL这三个之外还有一个Model存在,这个Model不属于任何一层,只是为了更好地链接三层而存在的。这个类只存储,与以上三类共同使用的东西。起一个协调的作用。Model类,也就是实体类Entity。

3.这几个层次的关系:

在这里插入图片描述

二、源代码分析

Model类:是为了封装数据的,为了在三层之间传输数据,不会引用任何一个程序集,但是其他三个都引用他

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

namespace Login.Model//是为了封装数据的,为了在三层之间传输数据,不会引用任何一个程序集,但是其他三个都引用他
{
   public  class UserInfo
    {
        public int Id { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public string Email { get; set; }

    }
}

UI 层:登录

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;//Windows.Froms的命令空间

namespace LoginUI
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            //初始化窗体上的所有控件
            InitializeComponent();
        }

        private void btnLogin_Click(object sender, EventArgs e)
        {
           
            //获得登录窗体的用户名 Trim去空格操作,注意和VB的形式不一样
            string userName = txtUserName.Text.Trim();

            //因为密码中可能是空格所以不用去空格操作
            string password = txtpassword.Text;

            //实例化业务逻辑层的对象mgr,让业务逻辑层去判断输入内容
            Login.BLL.LoginManager mgr = new Login.BLL.LoginManager();

            //用户输入的内容通过mgr调用业务逻辑层的UserLogin方法,属于拿来主义。B层会去调用D层
            mgr.UserLogin(userName, password);

            //和Model 交互,将数据层的信息传入Model
            Login.Model.UserInfo user = mgr.UserLogin(userName ,password );

            //登录成功提示登录成功
            MessageBox.Show("登录用户:"+userName );

        }
    }
} 

注意:UI 代码中(B层和D层也有)涉及到与B层交互,以及与Model层交互,代码表示为:Login.BLL.LoginManager 和 Login.Model.UserInfo,这里有个要讨论的点:
为什么要写成命名空间+类名?直接把命名空间用using引用进来可以吗?
在这里插入图片描述
答:直接引入命名空间是可以的,并且直接引入命名空间之后,涉及到这两类的所有对象,都不用再写其命名空间+类名了,这样就体现了一个复用的思想,不用每次都写命名空间。

这也就是类和类之间交互,需要跨类库找类时一个路径问题,先找到类所在的位置,再找类名。当然,我们在同一层(同一个类库)下,调用不同类的对象,就不需要先找路径(不需要写命名空间),这就像我们在同一个文件下找不同文件,他们的路径都是一样的,不需要我们再去找路径。
在这里插入图片描述
B层:

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

namespace Login.BLL//业务逻辑层:要与数据访问层交互,去调数据库,从而判断用户输入的账户密码是否数据库中存在
{
    public class LoginManager//登录管理
    {
        //定义一个方法UserLogin,返回值是Login.Model.UserInfo类型的对象
        public Login.Model.UserInfo UserLogin(string userName,string password)
        {
            
            //先实例化一个D层的对象
            Login.DAL.UserDAO uDao = new Login.DAL.UserDAO();

            //通过UI中填写的内容,调用SelectUser方法,返回相应的数据
            Login.Model.UserInfo user =uDao.SelectUser(userName ,password );//返回了user,含数据库中的内容

            if(user!=null)//登录成功
            {
                //登录成功,便给此用户加10分,并且返回user
                //与D层交互,实例化D层 一个加分对象
                Login.DAL.ScoreDAO sDao = new Login.DAL.ScoreDAO();
                //调用加分方法,加10分
                sDao.UpdateScore(userName, 10);
                return user;
            }
            else //若数据库中没有该用户名,则登录失败
            {
                throw new Exception("登录失败");
            }
        }
    }
}

D层:三个类:

class DbUtil:用于保存连接服务器的SQL语句
class UserDAO:访问数据库—添加积分:插入语句
class ScoreDAO:访问数据库—用户信息:查询语句

class DbUtil:

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

namespace Login.DAL
{
    class DbUtil//用于保存 链接服务器的SQL语句
    {
        public static string ConnString = @"Server=DESKTOP-4LVQ49K;Database=Login;uid=sa;Password=123456";

    }
}

提个问题:D 层中的这个DbUtil类有什么用?为什么要单独写成一个类?

答:大家不难发现:这个类只是实现了一个连接数据库的功能,但是这个连接数据库的语句很长,并且在UserDAO类和ScoreDAO类中都需要连接数据库,所以在这里把连接数据库的语句抽出来,封装成一个类,用一个string类型的 ConnString 对象接收,之后需要连接数据库时,只需要调用这个对象就可以了!

class UserDAO:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Login.Model;
using System.Data;
using System.Data.SqlClient;//SqlConnection的命名空间

namespace Login.DAL
{
    public class UserDAO//数据访问层---访问用户信息
    {
        public Login.Model.UserInfo SelectUser(string userName, string password)
        {   //using 是为了自动释放()里边的资源
          using (SqlConnection conn = new SqlConnection(DbUtil.ConnString))//实例化了一个连接数据库的对象,类似于一个开数据库的钥匙
          {
                SqlCommand cmd = conn.CreateCommand();//在现有连接conn的基础上,创建一个命令用以执行SQL指令。
                conn.Open();//连接(数据库的)打开

                //	CommandText:获取或设置要在数据源中执行的 Transact-SQL 语句、表名或存储过程。
                cmd.CommandText = @"SELECT ID,UserName,Password,Email FROM USERS WHERE UserName=@UserName AND Password=@Password";//执行的select语句

                //CommandType:获取或设置一个值,该值指示解释 CommandText 属性的方式。
                cmd.CommandType = CommandType.Text;//CommandType是一个枚举类型,有三个值:text、StoredProcedure(存储过程)、TableDirect用于表示SqlCommand对象CommandType的执行形式,这里是text

                //把用户输入的内容存到Parameters集合中
                cmd.Parameters.Add(new SqlParameter("@UserName", userName));
                cmd.Parameters.Add(new SqlParameter ("@Password",password ));

                SqlDataReader reader = cmd.ExecuteReader();//ExecuteReader :尽可能快地对数据库进行查询并得到结果。

                //声明一个Login.Model.UserInfo 类型的对象user,只是声明
                Login.Model.UserInfo user = null;
              
               while (reader.Read())//把数据库中读到的信息给user对象,返回给B层
                {
                    if (user == null)
                    {
                        user = new Login.Model.UserInfo();//实例化user
                    }
                    user.Id = reader.GetInt32(0);
                    user.UserName = reader.GetString(1);
                    user.Password = reader.GetString(2);//not suggestion
                    if (!reader.IsDBNull(3))
                    {
                        user.Email = reader.GetString(3);
                    }

                }
                return user;
            }
            

        }
    }
}

class ScoreDAO:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
namespace Login.DAL
{
    public class ScoreDAO
    {
        public void UpdateScore(string userName, int value)
        {
            using (SqlConnection conn = new SqlConnection(DbUtil.ConnString))
            {
                SqlCommand cmd = conn.CreateCommand();
				conn.Open();
				
                cmd.CommandText = @"INSERT INTO SCORES(UserName,Score) Values(@UserName,@Score)";//插入积分
                
				//把用户输入的内容存到Parameters集合中
                cmd.Parameters.Add(new SqlParameter("@UserName", userName));

                cmd.Parameters.Add(new SqlParameter("@Score", value));
                //SqlCommand.ExecuteNonQuery()方法:对连接执行SQL语句并返回受影响的行数。
                cmd.ExecuteNonQuery();
            }
        }

    }
   
}

提个问题三个问题:
✦问题一:using(){}的用法:

using (SqlConnection conn = new SqlConnection(DbUtil.ConnString))
{
...
}

对于这个问题的详细解释,请看小编的下篇博客:
https://blog.csdn.net/Ginny97/article/details/103943672

✦问题二:System.data和System.Data.Sqlclient命名空间:

System.data:即表示你的命名空间下有需要使用数据、数组的地方,可以直接使用数组类型,而不需要再添加前缀。
最常见的地方应该是ADO.NET数据层(即操作数据库的类)

System.Data.Sqlclient:表示在你的代码中引入微软发布的sqlserver数据库的ado.net程序集,引入后,你就可以使用SqlConnection、SqlCommand等数据库对象来访问sqlserver数据库。

✦问题三:关于cmd.Parameters.Add()的理解:
SqlParameter是什么:它表示SqlCommand的参数,以及可选的到DataSet列的映射。这个类不能被继承。(SqlCommand:表示要针对SQL Server数据库执行的Transact-SQL语句或存储过程。这个类不能被继承。DataSet:表示内存中的数据缓存。)

具体的作用和使用,可以参照这位博主的博客:感谢她的分享,也让小编学习到了:https://blog.csdn.net/hsm_Jasmine/article/details/103339498

好了,小编对于三层的学习暂时理解到这里了,下一站,七层机房重构走起!。◕‿◕。

发布了72 篇原创文章 · 获赞 17 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Ginny97/article/details/103985243
今日推荐