导航

 .NET Framework 2.0 版中新增的 TransactionScope 单独使用确实很方便。但是在实际项目中都有自己的访问层,如何才能和自己的数据访问层结合起来使用呢? 
           在项目中我是这样处理数据的:

  /// <summary>
    
/// 外包业务访问类
    
/// </summary>

    public class OutSourcingDAO
    
{

        
/// <summary>
        
/// 增加
        
/// </summary>
        
/// <param name="bt"></param>
        
/// <returns></returns>

        public int InsertGetIdentity(OutSourcing bt)
        
{
            
return new DAHelper(DataAccess.Create()).InsertGetIdentity(bt);
        }

        
/// <summary>
        
/// 更新
        
/// </summary>
        
/// <param name="bt"></param>
        
/// <returns></returns>

        public int Update(OutSourcing bt)
        
{
            
return new DAHelper(DataAccess.Create()).Update(bt);
        }

        
/// <summary>
        
/// 删除
        
/// </summary>
        
/// <param name="bt"></param>
        
/// <returns></returns>

        public int Delete(OutSourcing bt)
        
{
            
return new DAHelper(DataAccess.Create()).Delete(bt);
        }

    }

上面 OutSourcing 为与表相对应的实体类 ;DAHelper 为作者自己实现的一个包装类,可以对任意实体进行增加,修改,删除 查询等功能。
  再贴一段代码:

   public int ExecSql(string strSql)
        
{
            
try
            
{
                
this.OpenConnection();
                cmd.CommandType 
= CommandType.Text;
                cmd.CommandText 
= strSql;
              
               
return cmd.ExecuteNonQuery();
            }

            
catch (System.Exception e)
            
{
                
throw this.CatchException(e);
            }

            
finally
            
{
                
this.CloseConnection();
            }

        }

我最终通过调用 ExecSql  方法来与数据库交互,而该方法会自己打开数据库连接,执行语句,然后关闭连接。
在操作同一个数据库的时候,如果要高效的使用 TransactionScope,必须保证 SqlConnection 不改变,即用同一个 SqlConnection 来完成所需要的增加
删除,或修改。我想写这样的代码进行事务控制:

 public int UpdateTest(OutSourcing outSourcing, BusinessAccept businessAccept)
        
{
            IDataAccess dac 
= DataAccess.Create();
            DAHelper myHelper 
= new DAHelper(dac);
            
using (TransactionScope ts = new TransactionScope())
            
{
                myHelper.Update(outSourcing);
                myHelper.Update(businessAccept);
                
                ts.Complete();
            }

        }

这样就需要,执行第一个操作时候打开数据库连接,执行,不关闭连接,然后执行第二个操作,执行完关闭。显然,我想让 TransactionScope
实行 Dispose() 方法的时候关闭数据库连接。using 代码块的本质上等同于 try{}finally{} 语句代码块。为什么不封装一下 TransactionScope 让它满足自己的要求呢?

/// <summary>
/// TransactionScope 包装类
/// </summary>

public sealed class Scope : IDisposable
{
    
private TransactionScope m_TransactionScope = null;
    
/// <summary>
    
///  测试访问类
    
/// </summary>

    private DataAccessTest m_DataAccessTest = null;
    
/// <summary>
    
/// 实例化一个新的 TransactionScope
    
/// </summary>
    
/// <param name="dac"></param>

    public Scope(DataAccessTest dac)
    
{
        
this.m_DataAccessTest = dac;
        
//告诉访问类 你已经使用了事务
        dac.SetScope(this);
        
this.m_TransactionScope = new TransactionScope();

    }

    
/// <summary>
    
/// 发出事务结束命令
    
/// </summary>

    public void Complete()
    
{
        
this.m_TransactionScope.Complete();
    }

    
IDisposable 成员
}


数据库访问类代码如下:

/// <summary>
/// 模拟数据库访问类
/// </summary>

public class DataAccessTest
{
    SqlConnection con 
= null;
    SqlCommand cmd 
= new SqlCommand();
    Scope scope 
= null;
    
string strCon = "这里是数据库连接字符串。。。。。";

    
public void SetScope(Scope scope)
    
{
        
this.scope = scope;
    }

    
private void OpenConnection()
    
{

        
if (con == null || scope == null)
        
{
            con 
= new SqlConnection(strCon);
            cmd.Connection 
= con;
            con.Open();
            Console.WriteLine(
" 打开数据库连接;");
        }

    }


    
private void CloseConnection()
    
{
        
this.cmd.Parameters.Clear();
        
if (scope == null)
        
{
            
// 
            con.Close();
            con.Dispose();
            Console.WriteLine(
" 未使用事务 关闭数据库连接;");
        }

    }


    
public int ExecuteSql(string strSql)
    
{
        
try
        
{
            
this.OpenConnection();
            cmd.CommandType 
= CommandType.Text;
            cmd.CommandText 
= strSql;
            Console.WriteLine(
"执行 Sql 语句。。。");

            
return cmd.ExecuteNonQuery();
        }

        
catch (System.Exception e)
        
{
            
throw e;
        }

        
finally
        
{
            
this.CloseConnection();
        }

    }

 
    
public void Close()
    
{
        con.Close();
        con.Dispose();
        Console.WriteLine(
" 关闭数据库连接->该方法由 Scope中的Dispose()方法调用 ");
    }

}

赶快写个方法测试一下吧!

   /// <summary>
        
/// 测试
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>

        private void button1_Click(object sender, EventArgs e)
        
{
            
try
            
{
                Console.WriteLine(
"下面是使用事务的输出。。。。。。。。");
                
this.TestACT();
                Console.WriteLine(
"*********************下面是  未  使用事务的输出。。。。。。。。");
                
this.TestNoACT();
            }

            
catch (System.Exception ex)
            
{
                Console.WriteLine(
"出现了异常????????????");
                MessageBox.Show(ex.ToString());
            }


        }

        
/// <summary>
        
/// 使用事务
        
/// </summary>

        public void TestACT()
        
{
            DataAccessTest dac 
= new DataAccessTest();
            
using (Scope scope = new Scope(dac))
            
{
                
string strSql1 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('a')";
                
string strSql2 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('b')";
                dac.ExecuteSql(strSql1);
                dac.ExecuteSql(strSql2);

                scope.Complete();
            }

        }

        
/// <summary>
        
/// 不使用事务
        
/// </summary>

        public void TestNoACT()
        
{
                DataAccessTest dac 
= new DataAccessTest();
         
                
string strSql1 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('a')";
                
string strSql2 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('b')";
                dac.ExecuteSql(strSql1);
                dac.ExecuteSql(strSql2);
        }



查看一下输入的结果
下面是使用事务的输出。。。。。。。。
 打开数据库连接;
执行 Sql 语句。。。
执行 Sql 语句。。。
 关闭数据库连接->该方法由 Scope中的Dispose()方法调用
*********************下面是  未  使用事务的输出。。。。。。。。
 打开数据库连接;
执行 Sql 语句。。。
 未使用事务 关闭数据库连接;
 打开数据库连接;
执行 Sql 语句。。。
 未使用事务 关闭数据库连接
;

输出结果和想像中的完全一样 数据库也正确(没任何原因不正确!)
最后 改变一下方法:

   /// <summary>
        
/// 使用事务
        
/// </summary>

        public void TestACT()
        
{
            DataAccessTest dac 
= new DataAccessTest();
            
using (Scope scope = new Scope(dac))
            
{
                
string strSql1 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('a')";
                
string strSql2 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('b22222222222222222222222222222222222222222222222222222222222222222222222222222222222222')";
                dac.ExecuteSql(strSql1);
                dac.ExecuteSql(strSql2);

                scope.Complete();
            }

        }

熟悉数据库的肯定知道 “将截断字符串或二进制数据”吧!
输出结果为:
 下面是使用事务的输出。。。。。。。。
 打开数据库连接;
执行 Sql 语句。。。
执行 Sql 语句。。。
 关闭数据库连接->该方法由 Scope中的Dispose()方法调用
出现了异常????????????
数据库一条记录没有增加 数据库连接已经关闭  测试结果完全满意!
对自己访问层稍做修改:
最终的代码为:

   public int UpdateTest(OutSourcing outSourcing, BusinessAccept businessAccept)
        
{
            IDataAccess dac 
= DataAccess.Create();
            DAHelper myHelper 
= new DAHelper(dac);
            
using (Scope ts=new Scope())
            
{
                myHelper.Update(outSourcing);
                myHelper.Update(businessAccept);
                
                ts.Complete();
            }

        }