当项目大量使用ADO.NET的时候,通常我们会用一个DBHelper封装对数据库的操作,简化ADO.NET的调用。
class DBHelper { private const string connectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=17bang;Integrated Security=True;"; }
因为它是:
.NET Framework项目,习惯将连接字符串放在App.config或Web.config中
为了和ADO.NET原有的方法名(ExecuteNonQuery)相区别,提高代码的可读性,飞哥建议分列以下三类方法:
@想一想@:应该传入什么参数呢?
提示:方法体中封装不变/通用的部分(connection对象和它的打开/执行/关闭流程等),参数封装变化/个性的部分(具体命令)。
理想的操作是能把connection封装在Entity(比如Student)中,为客户调用屏蔽数据库连接细节:
public int Insert(DbCommand command) { using (SqlConnection connection = new SqlConnection(_connectionString)) { connection.Open(); // } }
以Insert()为例,至少可以这些重载:
public int Insert(string cmdText) { DbCommand command = new SqlCommand(cmdText); return Insert(command); } public int Insert(DbCommand command) { throw new NotImplementedException(); } public int Insert(string cmdText, params DbParameter[] parameters) { DbCommand command = new SqlCommand(cmdText); command.Parameters.AddRange(parameters); return Insert(command, parameters); }
注意:这些重载方法的相互调用关系。千万不要在每个方法里又都ADO.NET实现一遍!
@想一想@:Update()和Delete()可以互相调用么?
ExecuteReader()返回的DbDataReader,必须在connection打开的时候才能读取,所以:
不信邪的可以@试一试@,^_^
前面我们封装了
使用上述封装,如果我们进行批量操作的时候(比如一次性插入若干条数据),会不断的new出新的数据库连接对象,虽然有连接池,但也没有这种必要。
然而,connection从哪里来?还是要从DBHelper来!(因为只有DBHelper才有连接字符串)
//用方法而不是属性,并加一个A强调:Connection对象是方法体内直接生成的 public DbConnection GetAConnection() { return new SqlConnection(_connectionString); }
一种解决办法是额外增加一个InsertRange()方法,专门用于批量操作:
public void InsertRange(params string[] cmdText) { using (SqlConnection connection = new SqlConnection(_connectionString)) { for (int i = 0; i < cmdText.Length; i++) { Insert(cmdText[i]); } } }
这样就可以在一个连接下,完成全部操作。
但是,这样做有以下一些问题:
个人认为:暴露connection是一种比较好的方法。它甚至还顺带解决了DataReader和Transaction的问题,不需要额外的封装。
统一:暴露的connection没有Open()
所以另外一个解决方案是:
首先,Insert()方法可以接受一个connection对象参数:
/// <param name="connection">如果null,将由DBHelper自己生成一个connection</param> public int Insert(DbCommand command, DbConnection connection = null) //用户可以选择是否传入connection对象
然后,在方法内部确定好要使用的connection:
//决定是否在方法内Dispose()这个connection if (command.Connection == null) { command.Connection = new SqlConnection(_connectionString); command.Connection.Open(); }//else nothing
这个connection对象从外部传入,就由外部负责关闭,所以就不能再使用using了:
//不能使用using了 try { command.ExecuteNonQuery(); } catch (Exception) { //自定义异常处理,或者 throw; } finally { if (innerConn) { connection.Dispose(); }//else connection should be handled by invoker }
这同样需要在外部传入connection,最后,由调用方控制连接的关闭,比如:
public void Save(params Student[] students) { DBHelper helper = new DBHelper(); string cmd = "INSERT Student VALUES(....)"; using (DbConnection connection = helper.GetAConnection()) { for (int i = 0; i < students.Length; i++) { helper.Insert(cmd, connection); } } }
真实项目中的DBHelper,还会包含异常处理、事务、存储过程调用……要处理各种各样的情况,会更加复杂。
@想一想@:DBHelper能不能被多种数据库重用?为什么?
作业:
用DBHelper重用封装之前作业,可以提供:
多快好省!前端后端,线上线下,名师精讲
更多了解 加: