C#连接SQL Server数据库,并解决 “已有打开的与此 Command 相关联的 DataReader,必须首先将它关闭”

C#连接SQL Server数据库,并解决 “已有打开的与此 Command 相关联的 DataReader,必须首先将它关闭”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
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

posted @   Domefy  阅读(1772)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示