当项目大量使用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重用封装之前作业,可以提供:
	
	
多快好省!前端后端,线上线下,名师精讲
更多了解 加: