键盘敲烂,月薪过万作业不做,等于没学
当前系列: 垃圾桶 修改讲义

当项目大量使用ADO.NET的时候,通常我们会用一个DBHelper封装对数据库的操作,简化ADO.NET的调用。


连接字符串

需要封装在DBHelper中:
    class DBHelper
    {
        private const string connectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=17bang;Integrated Security=True;";
    }

因为它是:

  • 敏感的:不能泄露,所以private
  • 固定的:除非更改数据库,否则不会改变,所以const
  • 必须的:DBHelper中的其他方法可以方便的直接获取


配置文件

.NET Framework项目,习惯将连接字符串放在App.config或Web.config中


增删改三大方法

为了和ADO.NET原有的方法名(ExecuteNonQuery)相区别,提高代码的可读性,飞哥建议分列以下三类方法:

  • 增Insert():可以返回插入行的自增Id
  • 删Delete():返回被删除的行数
  • 改Updete():返回同Delete()

@想一想@:应该传入什么参数呢?

提示:方法体中封装不变/通用的部分(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()可以互相调用么?



ExeuteScalar

排除ExecuteReader()

ExecuteReader()返回的DbDataReader,必须在connection打开的时候才能读取,所以:

  • 不好封装,而且
  • 封装的价值也不大

不信邪的可以@试一试@,^_^



暴露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]);
            }
        }
    }

这样就可以在一个连接下,完成全部操作。

但是,这样做有以下一些问题:

  • 额外的增加了一些方法,不仅仅是Insert(),Delete()和Update()都有批量操作的需求
  • 不能解决使用一个connection,完成混合了Insert(),Delete()和Update()不同操作的需求


暴露连接

@想一想@:我们之前排除了ExecuteReader(),但是ExecuteReader()仍然需要被执行,仍然需要connection对象,这个connection对象从何而来?

个人认为:暴露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重用封装之前作业,可以提供:

  1. 封装过后的ExecuteNonQuery()/ExecuteScalar()/ExecuteReader()方法
  2. 可以在一个连接中完成若干操作,比如:标记勾选的Message为已读
  3. 事务的支持



学习笔记
源栈学历
大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 垃圾桶 中继续学习:

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码