在前一段时间,我写过一篇在机房收费系统使用事务的一篇博客,下面是链接地址
http://blog.csdn.net/yjjm1990/article/details/7347853
守宏师兄(http://blog.csdn.net/lsh6688)仔细看过之后,给出了自己的观点,并且指出了代码中的不足处。下面我将细细道来,希望对博友有些帮助,并希望大家提出宝贵意见!
首先要说的是SQLHelper这个数据库助手类,在我的原文中,处理事务的方法是和执行非查询语句一起执行的。其实可以感觉到,这里就有了代码的“坏味道”,我们都知道职责单一原则。职责单一原则是对类而言的,而类又是由方法组成的,那么一个方法当然也应该做到职责单一。而在第一篇博文中的SQLHelper类里的ExecuteNonQuery其实就拥有两个功能,一个是执行含有事务的SQL;另一个是执行不含事务的SQL。这个判断是放在了SQLHelper类里,其实一个比较好的处理方法是把这个方法分成两个方法,一个是执行含有事务的SQL语句或存储过程,一个是执行不含有事务的SQL语句或存储过程。
那么判断要怎么处理呢?
最好是放在D层去处理。
下面是一个方法清晰,功能完善的数据库助手类SQLHelper
using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Collections; using System.Configuration; namespace SqlHelp { public class SqlHelper { private string strCon; private SqlConnection SqlCon;//定义连接 private SqlDataReader sDr;//定义dataReader private SqlCommand sCmd;//定义Commond /// <summary> /// xml中连接字符串 /// </summary> /// <param name="connName"></param> public SqlHelper(string connName) { strCon = ConfigurationManager.ConnectionStrings[connName].ConnectionString; } /// <summary> /// 执行sql语句(事务中) /// </summary> /// <param name="cmdText">在事务中执行的某个Sql语句或者存储过程</param> /// <param name="paras">参数</param> /// <param name="CmdType">类型</param> /// <param name="sqlconn">连接</param> /// <param name="sqlTran">事务</param> /// <returns>返回受影响的行数</returns> public int ExecNoSelect(string cmdText, SqlParameter[] paras, CommandType CmdType, SqlConnection sqlconn, SqlTransaction sqlTran) { sCmd = new SqlCommand(cmdText, sqlconn); sCmd.CommandType = CmdType; sCmd.Parameters.AddRange(paras); sCmd.Transaction = sqlTran; return sCmd.ExecuteNonQuery(); } /// <summary> /// 关闭数据库连接 /// </summary> private void Close() { SqlCon.Close(); } /// <summary> /// 连接数据库的私有方法 /// </summary> /// <returns></returns> private SqlConnection GetCon() { if (SqlCon.State == ConnectionState.Closed) { SqlCon.Open(); } return SqlCon; } /// <summary> /// 处理传过来的sql语句或者存储过程 /// </summary> /// <param name="cmdText">sql语句或者存储过程</param> /// <param name="ct">类型</param> /// <returns>返回查询的到的databable</returns> public DataTable ExecuteQuery(string cmdText, CommandType ct) { DataTable dt = new DataTable(); //创建连接 sCmd = new SqlCommand(cmdText, GetCon()); //设定类型 sCmd.CommandType = ct; using (sDr = sCmd.ExecuteReader(CommandBehavior.CloseConnection)) { dt.Load(sDr); sDr.Close(); } Close(); return dt; } /// <summary> /// 处理传递进来的带参数的sql语句或是存储过程,返回DataTable /// </summary> /// <param name="cmdText">带参数的sql语句或是存储过程</param> /// <param name="paras">sql参数</param> /// <param name="ct">类型</param> /// <returns>返回查询的到的datatable</returns> public DataTable ExecuteQuery(string cmdText, SqlParameter[] paras, CommandType ct) { DataTable dt = new DataTable(); //创建连接 sCmd = new SqlCommand(cmdText, GetCon()); //设定类型 sCmd.CommandType = ct; //添加参数 sCmd.Parameters.AddRange(paras); using (sDr = sCmd.ExecuteReader(CommandBehavior.CloseConnection)) { dt.Load(sDr); sDr.Close(); } Close(); return dt; } /// <summary> /// 执行传递进来的sql语句或是存储过程 /// </summary> /// <param name="cmdText">sql语句或是存储过程</param> /// <param name="ct">类型</param> /// <returns>返回受影响的行数</returns> public int ExecuteNonQuery(string cmdText, CommandType ct) { int res = 0;//用来存储该操作影响的行数 SqlConnection Sqlcon = GetCon(); using (sCmd = new SqlCommand(cmdText, Sqlcon)) { //设定类型 sCmd.CommandType = ct; res = sCmd.ExecuteNonQuery(); Close(); } return res; } /// <summary> /// 执行传递进来的带参数的sql语句或是存储过程 /// </summary> /// <param name="cmdText">sql字符串</param> /// <param name="paras">参数</param> /// <param name="ct">sql字符串的类型</param> /// <returns>返回受影响的行数</returns> public int ExecuteNonQuery(string cmdText, SqlParameter[] paras, CommandType ct) { int res = 0;//用来存储该操作影响的行数 SqlConnection Sqlcon = GetCon(); using (sCmd = new SqlCommand(cmdText, Sqlcon)) { //设定类型 sCmd.CommandType = ct; //添加参数 sCmd.Parameters.AddRange(paras); res = sCmd.ExecuteNonQuery(); Close(); } return res; } /// <summary> /// 批量插入数据 /// </summary> /// <param name="tableName">数据表名称</param> /// <param name="dt">要插入的数据</param> /// <returns></returns> public bool BatchInsertData(string tableName, DataTable dt) { //数据批量导入sqlserver,创建实例 System.Data.SqlClient.SqlBulkCopy sqlbulk = new System.Data.SqlClient.SqlBulkCopy(strCon); //目标数据库表名 sqlbulk.DestinationTableName = tableName; //数据集字段索引与数据库字段索引映射 for (int i = 0; i < dt.Columns.Count; i++) { sqlbulk.ColumnMappings.Add(i, i); } //导入 sqlbulk.WriteToServer(dt); sqlbulk.Close(); return true; } } }
还有一点想强调和提醒大家的是,同样在上一篇博文中,我的事务处理是B层的事务处理的时候进行的。当然有一点我们是可以肯定的,就是事务处理一定是放在B层。我的做法是直接在B层处理事务。(详见我的上一篇博客)
下面是师兄的意见:
那就是利用面向对象中的“抽象”,再抽象出一个事务管理类,用来管理B层的事务。这样就更加面向对象了。
下面是事务管理类
using System.Data; using System.Data.SqlClient; using System.Configuration; using System.Collections; using Entity; namespace DAL { public class TransactionManager { /// <summary> /// 定义连接 /// </summary> SqlConnection sqlConnection = new SqlConnection(); /// <summary> /// 定义事务 /// </summary> SqlTransaction sqlTransaction; /// <summary> /// 得到连接 /// </summary> /// <returns></returns> public SqlConnection GetConnection() { sqlConnection.ConnectionString = ConfigurationManager.ConnectionStrings["sqlconn"].ConnectionString; sqlConnection.Open(); return sqlConnection; } /// <summary> /// 开启事务 /// </summary> public SqlTransaction TranBegin() { sqlTransaction = sqlConnection.BeginTransaction(); return sqlTransaction; } /// <summary> /// 提交事务 /// </summary> public void TranCommit() { sqlTransaction.Commit(); } /// <summary> /// 回滚事务 /// </summary> public void TranRollback() { sqlTransaction.Rollback(); } /// <summary> /// 关闭连接 /// </summary> public void Close() { sqlConnection.Close(); } } }
通过和师兄讨论,我们认为两种方法都可以。第一种方法处理更加简单、方便,第二种方法更能体现面向对象的观点。
希望大家提出宝贵意见!!!