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
