C#連接SQL Server數據庫,並解決 “已有打開的與此 Command 相關聯的 DataReader,必須首先將它關閉”
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Data.SqlClient; namespace demoConnectOperation { class Program { static void Main(string[] args) { //連接數據庫 SqlConnectionStringBuilder scsb = new SqlConnectionStringBuilder(); scsb.DataSource = "127.0.0.1"; scsb.UserID = "sa"; scsb.Password = "playea"; scsb.InitialCatalog = "stu_db"; scsb.MultipleActiveResultSets = true;//解決已有打開的與此 Command 相關聯的 DataReader,必須首先將它關閉。 //創建連接 SqlConnection conn = new SqlConnection(scsb.ToString()); //打開連接 if (conn.State == ConnectionState.Closed) { conn.Open(); } //定義變量 string stu_Number = null; string stu_Class=null; string stu_Name=null; string stu_Sex=null; string stu_Age = null; bool isFlag = false; while (true) { Console.WriteLine("插入信息【1】;修改信息【2】;刪除信息【3】,請輸入你要的操作:"); int choice = int.Parse(Console.ReadLine()); switch (choice) { case 1: //插入學生信息 //'學號','班級','姓名','性別','年齡' Console.WriteLine("請輸入學號:"); stu_Number = Console.ReadLine(); Console.WriteLine("請輸入班級:"); stu_Class = Console.ReadLine(); //根據學號和班級判斷,學生信息是否存在 isFlag = ExistsStu(conn, stu_Number, stu_Class); if (isFlag) { Console.WriteLine("輸入的學生信息已經存在!"); break; } Console.WriteLine("請輸入姓名:"); stu_Name = Console.ReadLine(); Console.WriteLine("請輸入性別:"); stu_Sex = Console.ReadLine(); Console.WriteLine("請輸入年齡:"); stu_Age = Console.ReadLine(); //在SQL server數據庫中添加信息 //添加數據的操作 string strInsert = "INSERT INTO [stu_db].[dbo].[Students]([stuNumber],[stuClass],[stuName],[stuSex],[stuAge])" + "VALUES('" + stu_Number + "','" + stu_Class + "','" + stu_Name + "','" + stu_Sex + "','" + stu_Age + "')"; //進行數據庫操作 sqlChoice(strInsert, conn); break; case 2: //修改學生信息 //'學號','班級','姓名','性別','年齡' Console.WriteLine("請輸入學號:"); stu_Number = Console.ReadLine(); Console.WriteLine("請輸入班級:"); stu_Class = Console.ReadLine(); //根據學號和班級判斷,學生信息是否存在 isFlag = ExistsStu(conn, stu_Number, stu_Class); if (!isFlag) { Console.WriteLine("輸入的學生信息不存在!"); break; } Console.WriteLine("請輸入姓名:"); stu_Name = Console.ReadLine(); Console.WriteLine("請輸入性別:"); stu_Sex = Console.ReadLine(); Console.WriteLine("請輸入年齡:"); stu_Age = Console.ReadLine(); //根據學號和班級更改 信息 string strUpdate = "UPDATE [stu_db].[dbo].[Students] SET [stuNumber] = '" + stu_Number + "',[stuClass] = '" + stu_Class + "',[stuName] = '" + stu_Name + "',[stuSex] = '" + stu_Sex + "',[stuAge] = '" + stu_Age + "' WHERE stuNumber='" + stu_Number + "' AND stuClass='" + stu_Class + "'"; //進行操作 sqlChoice(strUpdate, conn); break; case 3: //根據學號和班級進行刪除 Console.WriteLine("請輸入學號:"); stu_Number = Console.ReadLine(); Console.WriteLine("請輸入班級:"); stu_Class = Console.ReadLine(); //根據學號和班級判斷,學生信息是否存在 isFlag = ExistsStu(conn, stu_Number, stu_Class); if (!isFlag) { Console.WriteLine("輸入的學生信息不存在!"); break; } //刪除 string sqlDelete = "DELETE FROM [stu_db].[dbo].[Students]WHERE stuNumber='" + stu_Number + "' AND stuClass='" + stu_Class + "'"; //進行操作 sqlChoice(sqlDelete, conn); break; default: Console.WriteLine("輸入操作指令有誤!"); break; } } } //執行數據庫語句操作 public static void sqlChoice(string sqlString,SqlConnection conn) { //輸出測試 Console.WriteLine("SQL插入語句:"); Console.WriteLine(sqlString); //連接數據表 SqlCommand comm = new SqlCommand(sqlString, conn); try { int rc = comm.ExecuteNonQuery(); if (rc == 1) { Console.WriteLine("學員信息操作成功!"); } else { Console.WriteLine("學員信息操作失敗!"); } } catch (Exception ex) { Console.WriteLine(ex.Message); } } //判斷用戶輸入學生信息是否存在 public static bool ExistsStu(SqlConnection conn,string stu_Number,string stu_Class) { //sql語句 string sqlSelect = "SELECT *FROM [stu_db].[dbo].[Students]WHERE stuNumber='" + stu_Number + "' AND stuClass='" + stu_Class + "'"; //執行操作 //連接數據表 SqlCommand comm = new SqlCommand(sqlSelect, conn); SqlDataReader dr = comm.ExecuteReader(); return dr.Read(); } } }
qlCommand在每次執行ExecuteNonQuery()方法之后,內部會生成一個空的DataReader對象,該對象只有在數據庫連接關閉之后,才會被釋放掉,加上上邊提到的,在DBHelper類中數據庫連接對象是一個靜態的全局變量。因為在同時調用API進行數據查詢時,在第一個查詢還未結束,數據庫連接對象還未關閉,第二個查詢卻已經開始查詢,所以才會出現DataReader沒有關閉的情況
方法有三:
第一種方法:
使用using的形式:
Using(SqlConnection sqlConn=new SqlConnection(“數據庫連接字符串”)
{
sqlConn.Open();
SqlCommand com=new SqlCommnand(sqlCon,”sql”);
/執行訪問數據庫操作代碼
}
Using指定了SqlConnection的對象作用的范圍,且是獨占使用的,當使用結束之后會自動將其進行釋放,所以也就很好的解決了上邊的問題,即使是同時多次訪問,也不會有DataReader未關閉的情況,因為只有一次訪問完,釋放之后才會進行下一次的訪問。
第二中方法:
在數據庫連接字符串中添加MultipleActiveResultSets=true即可,示例如下:
server=.;Integrated Security = true;database=Test;MultipleActiveResultSets=true;
SQL Server數據庫默認的只有一個活動的SqlDataReader,如果想要一個連接允許多個SqlDataReader,那就需要將MultipleActiveResultSets設置為true,其意義為:將數據庫連接設置可復用,即可供多個SqlCommand同時使用。
如果在 MARS 連接下提交兩個批處理,一個批處理包含 SELECT 語句,另一個包含 DML 語句,DML 可以在 SELECT 語句執行過程中開始執行。 但是,DML 語句必須運行完成,SELECT 語句才可以繼續執行。 如果兩個語句在相同事務下運行,讀取操作將看不到 DML 語句在 SELECT 語句開始執行后所作的任何更改。
如果打開了啟用了MARS會話連接,會創建一個邏輯會話,增加系統的開銷,為了減小系統開銷提升系統性能,SqlClient會將對MARS對話緩存在連接內,做多可緩存10個對話。
MARS的操作不是線程安全的。如果應用程序打開了兩個連接一個為MARS連接一個為一般連接,則這兩個連接分別位於獨立的連接池中。使用MARS之后,並非不再需要在應用程序中使用多個連接,如果應用程序需要對服務器真正的執行並行命令,還是需要建立多個連接的。
所以,總的來說,雖然這種方法很簡單,也能解決問題,但是還是不推薦使用這種方法的。
第三種方法:
之所以會出現上邊報出的錯誤,往往都是因為數據連接對象是靜態、全局的對象,相應有很多朋友為了避免多次的聲明、創建對象,干脆將該數據連接對象設置為靜態的全局的,這樣做的確可以省下不少的功夫,但是帶來的弊端也是顯而易見的,也就是再高並發操作的情況下,會出現上邊提到的錯誤。
因此,這里建議,將數據連接對象設置為局部的,且每次都new一個對象出來,這樣做不過是多創建了幾個對象,開了幾個連接罷了,但是,即使是高並發也不會出現DataReader未關閉的情況。此外,事實上只有在第一次進行數據連接比較耗費時間和性能之外,以后進行的連接操作,所耗費的時間幾乎是可以忽略不計的,因為SqlConnection還有連接池的機制,這也是下邊要講的一個內容。
————————————————
原文鏈接:https://blog.csdn.net/abrahamchen/article/details/86647978