【.NET 框架】—— Dapper框架的高級應用(二)


1.1.Dapper調用存儲過程

存儲過程是一組預編譯的SQL語句

使用存儲過程有以下優點:

1.允許模塊化程序設計,就是說只需要創建一次,在以后的程序中就可以調用該過程任意次。

2.允許更快地執行,如果某操作需要大量SQL語句或重復執行對應的SQL語句,存儲過程比SQL語句執行的更快。

3.減少網絡流量,例如一個需要數百行的SQL代碼的操作有一條執行語句完成,不需要在網絡中發送數百行代碼。

4.更好的安全機制,對於沒有權限執行存儲過程的用戶,也可以授權它們執行存儲過程。

1.1.1.使用Dapper調用無參數的存儲過程

這里我首先在數據庫中創建了一個基於DapperDemo數據庫的無參數的存儲過程:dbo.P_stuMarkInfo,具體的存儲過程語句已上傳至github:https://github.com/devyf/Dapper-.git

 

 

這里我基於wiform界面按鈕操作來調用我的后台存儲過程,

點擊“未通過考試學生名單”按鈕,會調用后台在數據庫對應創建的”dbo. P_stuMarkInfo”存儲過程:

 

 

對應后台的無參存儲過程點擊調用執行代碼:

/// <summary>
        /// 點擊按鈕,查詢未能通過考試的學生名單,調用無參數的存儲過程,默認及格線為60分
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            List<StuInfo> stuList = new List<StuInfo>();
            using (IDbConnection db = new SqlConnection(DBHelper.ConnString))
            {
                stuList = db.Query<StuInfo>("dbo.P_stuMarkInfo", //存儲過程的名稱
                    null, //存儲過程的參數
                    null, //事務對象
                    true, //是否緩存
                    null, //獲取或設置在終止執行命令的嘗試並生成錯誤之間的等待時間
                    CommandType.StoredProcedure //指定的sql語句為存儲過程類型
                    ).ToList();
            }

            if (stuList.Count > 0)
            {
                stuList.ForEach(stu => this.textBox1.Text += stu.StuName + " ");
            }
        }

執行效果如下:

 

1.1.2.使用Dapper調用有參數(有返回值)的存儲過程

這里我繼續添加sql來創建了一個有參數、有返回值的存儲過程,如下:

----存儲過程的創建:②帶參數的存儲過程
----筆試和機試的及格線由用戶指定,並且統計出未通過考試的人數
if exists (select * from sysobjects where name='P_stuMarkInfo1')
drop proc P_stuMarkInfo1
go
create proc P_stuMarkInfo1
@writeLevel int,  ----輸入參數:筆試及格線
@labLevel int,    ----輸入參數:機試及格線
@examNum int output     ----輸出參數:未通過考試的人數
as

select @examNum = count(*) from stuinfo where stuNo not in
(select stuNo from stumark where writtenExam >= @writeLevel and labExam>=@labLevel)


----執行存儲過程
declare @countNum int
exec P_stuMarkInfo1 60, 60, @countNum output

print '未通過考試的人數:' + convert(varchar(20), (@countNum)) 

存儲過程語句均已上傳至github:https://github.com/devyf/Dapper-.git

具體存儲過程創建時需要對應在當前數據庫下執行並保存,如下圖:

 

 

這里對應前台winform界面上輸入和輸出參數設置如下,點擊”未通過考試學生人數”按鈕可以調用后台的存儲過程:

 

 

后台調用有參數的存儲過程代碼如下:

/// <summary>
        /// 點擊按鈕,通過設置的筆試成績和機試成績去動態調用帶參數的存儲過程
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            //准備存儲過程的三個參數:兩個是輸入參數,一個是輸出參數
            var param = new DynamicParameters();  //動態參數類
            try
            {
                param.Add("@writeLevel", int.Parse(this.writeLev.Text)); //存儲過程的輸入參數賦值
                param.Add("@labLevel", int.Parse(this.labLev.Text));
                param.Add("@examNum", 0, DbType.Int32, ParameterDirection.Output); //標注為輸出參數
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
          

            using (IDbConnection db = new SqlConnection(DBHelper.ConnString))
            {
                db.Execute("dbo.P_stuMarkInfo1",  //指定存儲過程名稱
                    param,  //存儲過程參數
                    null,  //存儲過程事務
                    null,  //執行等待時間
                    CommandType.StoredProcedure  //指定執行為存儲過程類型
                    );
                //通過參數調用Get方法來獲取返回值
                int outNum = param.Get<int>("@examNum");
                //放置到文本框中
                this.nopassNum.Text = outNum.ToString();
                MessageBox.Show("存儲過程執行成功!");
            }
        }

1.2.Dapper執行事務操作

事務的概念:在關系型數據庫中,一個事務可以是一條SQL語句,一組SQL語句或者整個程序。

事務特性:

事務是恢復和並發控制的基本單位。

事務應該具有4個屬性:原子性、一致性、隔離性、持久性。這四個屬性通常稱為ACID特性。

原子性(atomicity):一個事務是一個不可分割的工作單位,事務中包括的所有操作要么都做,要么都不做。

一致性(consistency):事務必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。

隔離性(isolation):一個事務的執行不能被其它事務干擾。即一個事務內部的操作及使用的數據對並發的其它事務是隔離的,並發執行的各個事務之間不能互相干擾。

持久性(durability):持久性也稱為永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。接下來的其它操作或故障不應該對其有任何影響。

使用Dapper操作數據庫事務,這里基於winform界面執行數據庫中兩張表(主表stuinfo、從表stumark)進行刪除操作,使用Dapper操作事務進行提交、刪除、回滾操作,具體后台代碼如下:

/// <summary>
        /// 刪除按鈕點擊執行事務操作
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void delBtn_Click(object sender, EventArgs e)
        {
            //執行界面操作,根據主表學生表的ID去刪除相關聯的信息
            //事務操作:根據輸入的學生ID編號,刪除從表,刪除主表中的數據信息列
            int delId = int.Parse(this.txtDelID.Text);  //取出要刪除的學生表的學號
            using (IDbConnection db = new SqlConnection(DBHelper.ConnString))
            {
                db.Open(); //基於事務操作的特殊性:執行事務之前需要優先開啟連接
                //try catch語句使用外側代碼:快捷鍵:ctrl+k、ctrl+s
                //創建事務對象
                IDbTransaction transaction = db.BeginTransaction();  //開啟數據庫的事務
                try
                {
                    //根據用戶輸入的學號ID進行刪除的操作:先刪除從表,再刪除主表的信息
                    string delSql1 = "delete from stuinfo where stuNo = @stuNo";  //主表
                    string delSql2 = "delete from stumark where stuNo = @stuNo";  //從表

                    //執行刪除操作
                    db.Execute(delSql2, new { stuNo = delId }, transaction, null, null);
                    db.Execute(delSql1, new { stuNo = delId }, transaction, null, null);

                    //提交事務
                    transaction.Commit();
                    MessageBox.Show("刪除成功!");
                }
                catch (Exception ex)
                {
                    //出現異常,需要回滾事務
                    transaction.Rollback();
                    MessageBox.Show("出現異常:" + ex.Message);
                }
                finally
                {
                    db.Close();
                }
            }
        }

執行刪除從表、主表操作,成功從前台界面上刪除ID為”1006”的數據:

 

 

對應數據庫中也隨即刪除:

 

 

 

 

對應如果交換delSql1與delSql2的執行順序,這時因為先刪除主表,而在從表上有數據外鍵的關聯,會執行數據回滾rollback操作,無法進行數據的刪除操作:

 

可以看到catch異常之后,數據庫中數據”1005”並未執行數據列的刪除操作:

 

1.3.Dapper進行多表查詢

Dapper框架可以基於數據庫sql語句進行數據庫字段映射,並使用splitOn進行類型與返回值之間的划分。

下面基於兩個實例來簡單介紹一下Dapper進行多表查詢返回值划分案例:

實例一:

①首先基於之前的dbo.stuinfo表與dbo.stumark表進行多表連接查詢,對應sql語句與查詢結果如下:

 

②基於這個連接查詢的結果,使用Dapper框架進行Query查詢封裝查詢映射,具體代碼如下:

private void button1_Click(object sender, EventArgs e)
        {
            using (IDbConnection db = new SqlConnection(DBHelper.ConnString))
            {
                var sql = "select * from stuinfo inner join stumark on stuinfo.stuNo = stumark.stuNo";

                //執行查詢:多表(類型一,類型二,返回值)
                var list = db.Query<StuInfo, StuMark, StuInfo>(
                    sql,
                    (students, score) => { return students; }, //變量students對應的StuInfo類型,scores對應StuMark類型
                    null,  //存儲過程的參數
                    null,  //事務
                    true,  //緩存
                    splitOn: "stuNo" //該參數是用來划分查詢中的字段是屬於哪個表的 splitOn可以省略
                    );

                /*splitOn:stuNo 划分查詢中的字段是屬於哪個表的,也就是查詢結構映射到哪個實體,上邊的sql運行時,會從查詢結果所有
                 字段列表的最后一個字段進行匹配,一直找到stuNo這個字段(大小寫不計),找到的第一個stuNo字段匹配就是Query參數中
                 StuInfo類的stuNo屬性,那么從stuNo到最后一個字段都屬於StuInfo,StuNo以前的字段都被映射到StuMark這張表
                 通過(T,P)=>(return T)把兩個類的實例解析出來*/
                this.dgvContent.DataSource = list;
            }
        }

執行界面點擊操作,加載數據庫查詢映射結果如下圖所示:

 

實例二:

繼續使用查詢語句進行數據分割操作,這里同樣創建了兩個數據點擊顯示按鈕界面進行查詢操作:

 

查看數據后台代碼:

 private void button1_Click(object sender, EventArgs e)
        {
            using (IDbConnection db = new SqlConnection(DBHelper.ConnString))
            {
                List<Customer> userList = new List<Customer>();
                string sql = @"select u.*, r.* from UserInfo u 
                               inner join UserRole ur on ur.UserId = u.UserId
                               inner join Role r on r.RoleId = ur.RoleId";
                userList = db.Query<Customer, Role, Customer>( //第三個參數是返回值類型
                    sql,
                    (user, role) => { user.Role = role; return user; },
                    null,
                    null,
                    true,
                    "RoleId",   //分割數據列字符串
                    null,
                    null
                    ).ToList();
                this.dataGridView1.DataSource = userList;
                //打印其中單個字符
                if (userList.Count > 0)
                {
                    userList.ForEach(item => Console.WriteLine("userName:" + item.Username + "passWord:" + item.PasswordHash +
                        "Role:" + item.Role));
                }
            }

        }

        private void button2_Click(object sender, EventArgs e)
        {
            using (IDbConnection db = new SqlConnection(DBHelper.ConnString))
            {
                List<User> userList = new List<User>();
                string sql = @"select u.UserId, u.UserName, u.PasswordHash, r.RoleId, r.RoleName from UserInfo u
                               inner join UserRole ur on ur.UserId = u.UserId
                               inner join Role r on r.RoleId = ur.RoleId";
                Dictionary<int, User> dic = new Dictionary<int, User>();
               
                userList = db.Query<User, Role, User>(
                    sql,
                    (user, role) => 
                    {
                        User tempUser;
                        if (!dic.TryGetValue(user.UserId, out tempUser))
                        {
                            tempUser = user;
                            dic.Add(user.UserId, tempUser);
                        }
                        tempUser.Role.Add(role);
                        return user;
                    },
                    null,
                    null,
                    true,
                    "RoleId",
                    null,
                    null
                    ).ToList();

                    this.dataGridView2.DataSource = userList;
                //打印其中單個字符
                if (userList.Count > 0)
                {
                    userList.ForEach(item => Console.WriteLine("userName:" + item.Username + "passWord:" + item.PasswordHash +
                        "Role:" + item.Role.First().RoleName));
                }
            }
        }

查詢結果如下圖所示:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM