三層登錄實例以及代碼詳解

一、實例:

現在有一個積分系統,學生每登錄一次可獲得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

好了,小編對於三層的學習暫時理解到這裏了,下一站,七層機房重構走起!。◕‿◕。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章