数据库开发(五)ADO.NET连接池、封装SQLHelper

作者 Zhendong Ho 日期 2019-05-31
C#
数据库开发(五)ADO.NET连接池、封装SQLHelper

ADO.NET连接池

启用ADO.NET连接池的效果

默认情况下ADO.NET连接池是被启用的。

string constr = "Data Source=.;Initial Catalog=MyDatabaseOne;Integrated Security=True";
//默认情况下.net启用了连接池
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 1000; i++)
{
using (SqlConnection con = new SqlConnection(constr))
{
con.Open();
con.Close();
}
}
watch.Stop();
Console.WriteLine(watch.Elapsed); // 00:00:00.2973565

禁用ADO.NET连接池的效果

在连接字符串中添加:Pooling=false。

string constr = "Data Source=.;Initial Catalog=MyDatabaseOne;Integrated Security=True;Pooling=False";
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 1000; i++)
{
using (SqlConnection con = new SqlConnection(constr))
{
con.Open();
con.Close();
}
}
watch.Stop();
Console.WriteLine(watch.Elapsed); // 00:00:08.0076677

通过观察发现:

  • 当启用ADO.NET连接池后,其实只会创建一个连接对象,并且该连接对象默认不会立刻关闭,后面的所有其他连接对象都是使用这个连接对象。
  • 禁用连接池后,每次创建一个连接对象,然后关闭连接。此时会真正地创建多个连接对象,并每次登陆、登出。

ADO.NET连接池使用注意事项

  • 当第一次连接数据库的时候,因为连接池中没有任何现成的可用连接对象,所以第一次要创建一个对象。
  • con.Close(),当调用Close()方法的时候,其实并没有关闭连接,而是把连接对象放入到了连接池中。
  • 当下次再需要连接数据库的时候,首先会检查本次连接所使用的连接字符串是否与连接池中现有的连接对象所使用的连接字符串一模一样,如果一样,则可以直接从连接池中取出该连接对象,直接使用。如果池中没有现成的连接对象,或者池中的连接对象所使用的连接字符串与本次连接要使用的连接字符串不一致,那么此时都会再创建一个新的连接对象。

凡是需要用到“池”的地方,一般都会存在两种情况:

  1. 创建对象比较费时。
  2. 对象使用比较频繁。

池的作用:提高了创建对象的效率。

ADO.NET连接池使用总结

  1. 第一次打开连接会创建一个连接对象。
  2. 当这个连接关闭时(调用Close()方法时)会将当前那个连接对象放入池中。
  3. 下一个连接对象,如果连接字符串与池中现有连接对象的连接字符串完全一致,则会使用池中的现有连接,而不会重新创建一个。
  4. 只有对象调用Close()的时候才会放入池中,如果一个连接对象一直在使用,则下次再创建一个连接对象发现池中没有,也会再创建一个新连接对象。
  5. 在池中的连接对象,如果过一段时间没有被访问则自动销毁。

封装SqlHelper

新增一个类,改名为SqlHelper.cs。

public static class SqlHelper
{
// 定义一个连接字符串
// readonly修饰的变量,只能在初始化的时候赋值,以及在构造函数中赋值,其他地方只能读取不能设置值
// 通过读取配置文件获取连接字符串
private static readonly string constr = ConfigurationManager.ConnectionStrings["mssqlserver"].ConnectionString;

// 1.执行增(insert)、删(delete)、改(update)的方法
// ExecuteNonQuery
public static int ExecuteNonQuery(string sql, CommandType cmdType, params SqlParameter[] pms)
{
using (SqlConnection con = new SqlConnection(constr))
{
using (SqlCommand cmd = new SqlCommand(sql, con))
{
cmd.CommandType = cmdType;
if (pms != null)
{
cmd.Parameters.AddRange(pms);
}
con.Open();
return cmd.ExecuteNonQuery();
}
}
}

// 2.执行查询,返回单个值的方法
// ExecuteScalar
public static object ExecuteScalar(string sql, CommandType cmdType, params SqlParameter[] pms)
{
using (SqlConnection con = new SqlConnection(constr))
{
using (SqlCommand cmd = new SqlCommand(sql, con))
{
cmd.CommandType = cmdType;
if (pms != null)
{
cmd.Parameters.AddRange(pms);
}
con.Open();
return cmd.ExecuteScalar();
}
}
}

// 3.执行查询,返回多行,多列的方法
// ExecuteReader
public static SqlDataReader ExecuteReader(string sql, CommandType cmdType, params SqlParameter[] pms)
{
SqlConnection con = new SqlConnection(constr);
using (SqlCommand cmd = new SqlCommand(sql, con))
{
cmd.CommandType = cmdType;
if (pms != null)
{
cmd.Parameters.AddRange(pms);
}
try
{
con.Open();
// System.Data.CommandBehavior.CloseConnection这个枚举参数,表示将来使用完毕SqlDataReader后,在关闭reader的同时,在SqlDataReader内部会将关联的Connection对象也关闭掉
return cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
}
catch
{
con.Close();
con.Dispose();
throw;// 抛出捕获到的异常,只能写在catch中
// throw new Exception("ExcuteReader出错了?"); 抛出一个新的自定义异常,可写在任何地方
}
}
}

// 4.查询数据返回DataTable
public static DataTable ExecuteDataTable(string sql, CommandType cmdType, params SqlParameter[] pms)
{
DataTable dt = new DataTable();
using (SqlDataAdapter adapter = new SqlDataAdapter(sql, constr))
{
adapter.SelectCommand.CommandType = cmdType;
if (pms != null)
{
adapter.SelectCommand.Parameters.AddRange(pms);
}
adapter.Fill(dt);
}
return dt;
}
}

连接字符串写在配置文件中。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="mssqlserver" connectionString="Data Source=.;Initial Catalog=MyDatabaseOne;Integrated Security=True" />
</connectionStrings>
</configuration>