• c# 轻量级 ORM 框架 之 DBHelper 实现 (三)


      周末了比较清闲,把自己的orm框架整理了下,开源了.

      已经做出来的东西通常感觉有些简单,一些新手或许听到"框架"一类的词觉得有些"高深",简单来说orm就是把ado的封装.

    在介绍这个框架的第一篇博文,已经把DalBase介绍了一下设计思路,本篇的DBHelper对象也是给dalBase来用的,可以说框架的所有定义对象都是为了它.

    这里起名叫DBHelper,因为我也是从写SQLHelper开始的,DBHelper只不过是所有类型对ado操作的各种方法的封装,所以本篇博文希望给c#新手,或是对ado.net认识比较模糊的有一些帮助.

    首先DBHelper定义是个抽象的,因为我不知道DalBase 到底要访问哪种数据库,但我知道不管哪中数据库都会有链接字符串,那我就要求,必须要有连接字符串.

    public DbHelperBase(string connStr)
            {
                _ConnStr = connStr;
            }
    View Code

    DbHelper是个抽象的,那它的成员必然就不能有具体对象.

    那就把ado.net常用的对象定义出来.(如果你是初学者,我的建议是 用到哪个对象再去定义,否则到后期自己都不知道定义它干啥呢)

            protected abstract DbConnection DBConnectionObj { get; }
            protected abstract DbCommand DbCommandObj { get; }
            protected abstract DbDataAdapter DbDataAdapterObj { get; }
            protected DbTransaction DbTransObj;
    
            public DbConnection CurrentConnection
            {
                get
                {
                    return DBConnectionObj;
                }
            }  

    说明一下,为什么事务对象不是抽象的,因为事务对象始终是有DbConnection来创建的,我不用知道它具体是什么类型.

    DbConnection 对象public也是为了扩展其它功能而存在的.

    定义了是否事务的变量,所有ado操作都会判断当前是否处于事务的标记.

    bool _IsTrans = false;

    初学.net的朋友,应该都会有一个SQLHelper的类,我也曾经看过都大同小异,而且普遍都没有事务的实现,如果你中枪了,那么恭喜你,你即将会改变你sqlHelper的实现.

    如下代码:

         /// <summary>
            /// 执行一条指定命令类型(SQL语句或存储过程等)的SQL语句,返回所影响行数
            /// </summary>
            public int ExecNonQuery(string sqlText, CommandType cmdType, params DbParameter[] param)
            {
                using (SqlConnection conn = new SqlConnection(_ConnStr))
                {
                    using (SqlCommand cmd = new SqlCommand(sqlText, conn))
                    {
                        cmd.CommandType = cmdType;
                        if (param != null)
                            cmd.Parameters.AddRange(param);
                        conn.Open();
                        return cmd.ExecuteNonQuery();
                    }
                }
            }

    以上代码看起来是没什么问题,但如果要启用事务的话想想是否可以实现呢?

    如果说改造一下加上事务的代码就行的话

    如下:

     SqlTransaction tran = conn.BeginTransaction();
     cmd.Transaction = tran;   

    这样显然是错的,因为我们往往多条执行语句通常是分多次调用 ExecNonQuery() 方法的.

    这样的一个ado方法的封装显然是不合理的.

    如果多个增删改查(注意:查询也可能在是事务里)的方法,在一个事务里,那么必须是一个数据库连接(Connection)

    这就是为什么把那三个对象定义到外面的原因之一了,最重要的原因是我需要子类去重写它.

    下面看看我的实现:

            /// <summary>
            /// 打开连接,如果已经打开则什么都不执行了
            /// </summary>
            void OpenConnection()
            {
                if (DBConnectionObj.State != ConnectionState.Open)
                {
                    DBConnectionObj.ConnectionString = _ConnStr;
                    DBConnectionObj.Open();
                }
            }

    /// <summary> /// 给当前DbCommand对象赋值,并且OpenConnection(); /// </summary> void SetCommandAndOpenConnect(string sqlText, CommandType cmdType, params DbParameter[] param) { //按说赋值Connection,CommandType,是不用多次赋值的 DbCommandObj.CommandType = cmdType; DbCommandObj.Connection = DBConnectionObj; DbCommandObj.Parameters.Clear(); if (param != null) { DbCommandObj.Parameters.AddRange(param); } DbCommandObj.CommandText = sqlText; OpenConnection(); }
            /// <summary>
            /// 执行一条指定命令类型(SQL语句或存储过程等)的SQL语句,返回所影响行数
            /// </summary>
            public int ExecNonQuery(string sqlText, CommandType cmdType, params DbParameter[] param)
            {
                try
                {
                    SetCommandAndOpenConnect(sqlText, cmdType, param);
                    return DbCommandObj.ExecuteNonQuery();
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    CloseConnect();
                }
            }

    看到这三个方法或许对于初学者会感到迷茫了,没有看到任何有关事务的代码呢,兜个圈子,现在想象一下如果加事务的话,需要做什么?

    我们先从理论上认识一下,事务处理的流程

    1.指定事务是哪个Connection

    2.Command的事务对象指定到该事务.

    3.Open()

    4.提交或回滚(我是能写汉字的地方绝不写拼音)

    5.关闭连接.

    继续说这几个方法,为什么定义SetCommandAndOpenConnect 和 OpenConnection 这两个方法,本着尽量减少重复代码的原则.仅此而已.

    既然Connection和Command都已经定义到方法外了,那就是说我只要再执行Command.ExecuteNonQuery()方法前,给他们赋值就行了.

    也就是开始事务只需要给这个两个对象赋值即可

    事务的相关代码如下:

            /// <summary>
            /// 开始执行事务
            /// </summary>
            public void TransStart()
            {
                OpenConnection();
                DbTransObj = DBConnectionObj.BeginTransaction();
                DbCommandObj.Transaction = DbTransObj;
                _IsTrans = true;
            }
            /// <summary>
            /// 事务提交
            /// </summary>
            public void TransCommit()
            {
                _IsTrans = false;
                DbTransObj.Commit();
                CloseConnect();
            }
            /// <summary>
            /// 事务回滚
            /// </summary>
            public void TransRollback()
            {
                _IsTrans = false;
                DbTransObj.Rollback();
                CloseConnect();
            }

    这就是事务的方法了.

    最后一个CloseConnect()方法,差点把它遗忘了

            /// <summary>
            /// 关闭连接,如果没有开始事务或连接打开时才关闭
            /// </summary>
            void CloseConnect()
            {
                if (!_IsTrans)
                {
                    if (DBConnectionObj.State == ConnectionState.Open)
                    {
                        DBConnectionObj.Close();
                        DBConnectionObj.Dispose();
                    }
                }
            }

    当开始事务时,连接是不能关的.只有提交了或回滚了才会把当前连接断掉.

    到这里其实DbHelper的设计基本完成,再贴一下关于查询的几个方法,和执行类似就不解释了.

         /// <summary>
            /// 获得首行首列
            /// </summary>
            public object GetScalar(string sqlText, CommandType cmdType, params DbParameter[] param)
            {
                try
                {
                    SetCommandAndOpenConnect(sqlText, cmdType, param);
                    return DbCommandObj.ExecuteScalar();
    
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    CloseConnect();
                }
            }
         /// <summary>
            /// 执行一条SQL语句返回DataSet对象
            /// </summary>
            public DataSet GetDataSet(string sqlText, CommandType cmdType, params DbParameter[] param)
            {
                try
                {
                    SetCommandAndOpenConnect(sqlText, cmdType, param);
                    DbDataAdapterObj.SelectCommand = DbCommandObj;
                    DataSet ds = new DataSet();
                    DbDataAdapterObj.Fill(ds);
                    return ds;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    CloseConnect();
                }
            }
    
         /// <summary>
            /// 获得DataReader对象
            /// </summary>
            public DbDataReader GetDataReader(string sqlText, CommandType cmdType, params DbParameter[] param)
            {
                try
                {
                    SetCommandAndOpenConnect(sqlText, cmdType, param);
                    CommandBehavior cmdBehavior = CommandBehavior.CloseConnection;
                    if (_IsTrans)
                    {
                        cmdBehavior = CommandBehavior.Default;
                    }
                    DbDataReader dbReader = DbCommandObj.ExecuteReader(cmdBehavior);
                    return dbReader;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    //DataReader用dbReader对象来关闭
                    //CloseConnect();
                }
            }

    这里需要注意的是关于返回DataReader对象时不能关闭Connect,和 cmdBehavior 的赋值.

    好了,到这DBHelper的设计和核心代码已经全部实现了.

    试想一下我现在要完成SQLServerHelper的实现需要做的是什么?

    当然只要实现父类的那几个抽象属性就行了.

    代码如下:

     public class SQLHelper : DbHelperBase
        {
            public SQLHelper(string connStr)
                : base(connStr)
            { }
    
            SqlConnection _DBConnectionObj;
            SqlCommand _DbCommandObj;
            SqlDataAdapter _DbDataAdapterObj;
    
            protected override DbConnection DBConnectionObj
            {
                get
                {
                    //SqlBulkCopy aa = new SqlBulkCopy(new SqlConnection());
                    if (_DBConnectionObj == null)
                    {
                        _DBConnectionObj = new SqlConnection(_ConnStr);
                    }
                    return _DBConnectionObj;
                }
            }
    
            protected override DbCommand DbCommandObj
            {
                get
                {
                    if (_DbCommandObj == null)
                    {
                        _DbCommandObj = new SqlCommand();
                    }
                    return _DbCommandObj;
                }
            }
    
            protected override DbDataAdapter DbDataAdapterObj
            {
                get
                {
                    if (_DbDataAdapterObj == null)
                    {
                        _DbDataAdapterObj = new SqlDataAdapter();
                    }
                    return _DbDataAdapterObj;
                }
            }
    
        }
    View Code

    OracleHelper,oledbhelper,SQLiteHelper,就不贴代码了.

    转载建议标明出处(我从来不强迫别人做我管不了的事).

    整个框架的源码在介绍框架的第一篇博文里有.

    欢迎吐槽,点赞,建议,批评,指正,抄袭.

  • 相关阅读:
    CF 676C. Vasya and String 尺取经典题目
    进制转换
    《Dotnet9》系列-开源C# Winform控件库1《HZHControls》强力推荐
    《Dotnet9》系列-开源C# Winform控件库强力推荐
    《Dotnet9》系列-开源C# WPF控件库强力推荐
    《Dotnet9》系列-开源C# WPF控件库3《HandyControl》强力推荐
    《Dotnet9》系列-开源C# WPF控件库2《Panuon.UI.Silver》强力推荐
    《Dotnet9》系列之建站-Dotnet9建站20天感悟
    《Dotnet9》系列-开源C# WPF控件库1《MaterialDesignInXAML》强力推荐
    《Dotnet9》系列-FluentValidation在C# WPF中的应用
  • 原文地址:https://www.cnblogs.com/xtdhb/p/3812804.html
Copyright © 2020-2023  润新知