下面是我写的一个数据库操作类,每一个函数执行不同的数据库操作:
using System.Data.SqlClient;namespace CWMS.DataStore
{
public partial class Bid
{ public void do1()
{
SQL操作1
}
public void do2()
{
SQL操作2
} public void do3()
{
SQL操作3
}
public void do4()
{
SQL操作4
}
public void do5()
{
SQL操作5
}
}
}
下面是我在程序中调用这类进行相关数据库操作:
private CWMS.DataStore.Bid bid = new CWMS.DataStore.Bid();
public override void Submit()
{
……
bid.do5();
if (成功)
{
…
bid.do1();
…
bid.do2();
…
bid.do3();
…
bid.do4();
}
}
问题:
我想实现,对任一个函数如果没有执行成功,则rollback;
如果是在一个函数中用事务处理我会用,但像这种多个函数的事务处理操作应该怎么写呢?听说可以用函数传递事务参数的方法,但是不会用,请大家帮帮忙。
using System.Data.SqlClient;namespace CWMS.DataStore
{
public partial class Bid
{ public void do1()
{
SQL操作1
}
public void do2()
{
SQL操作2
} public void do3()
{
SQL操作3
}
public void do4()
{
SQL操作4
}
public void do5()
{
SQL操作5
}
}
}
下面是我在程序中调用这类进行相关数据库操作:
private CWMS.DataStore.Bid bid = new CWMS.DataStore.Bid();
public override void Submit()
{
……
bid.do5();
if (成功)
{
…
bid.do1();
…
bid.do2();
…
bid.do3();
…
bid.do4();
}
}
问题:
我想实现,对任一个函数如果没有执行成功,则rollback;
如果是在一个函数中用事务处理我会用,但像这种多个函数的事务处理操作应该怎么写呢?听说可以用函数传递事务参数的方法,但是不会用,请大家帮帮忙。
/// <summary>
/// delete the data from cstiim,cstmbm,cstpim,cstpnm,cstpcm,cstlrm,csterm,cstpfm
/// when delete Cost Set From csm
/// </summary>
/// <param name="strCost"></param>
/// <param name="strError"></param>
/// <returns></returns>
public static int DeleteDataCost(string strCost,out string strError)
{
int nRet = 0; DbAccess dbAccess = new DbAccess(strDatabaseLogicName);
dbAccess.Conn.Open();
IDbTransaction Transaction = dbAccess.Conn.BeginTransaction(); CstiimService service=new CstiimService();
try
{
CstiimSQL.DeleteDataCost(service, Transaction, strCost); CstmbmSQL.DeleteDataCost(service, Transaction, strCost); CstpimSQL.DeleteDataCost(service, Transaction, strCost); CstpnmSQL.DeleteDataCost(service, Transaction, strCost); CstpcmSQL.DeleteDataCost(service, Transaction, strCost); CstlrmSQL.DeleteDataCost(service, Transaction, strCost); CstermSQL.DeleteDataCost(service, Transaction, strCost); CstpfmSQL.DeleteDataCost(service, Transaction, strCost); CsmSQL.DeleteDataCost(service, Transaction, strCost);
}其中
public static void DeleteDataCost(Service service, IDbTransaction transaction, string strCost)
{
string strCommand = "delete from cstiim where cfcset='" + strCost + "'"; DbAccess dbAccess = new DbAccess(service.Database); try
{
dbAccess.ExecuteNonQuery(transaction, CommandType.Text, strCommand);
}
catch (Exception ex)
{
throw ex;
}
}
其余的都一样
CstiimService service=new CstiimService();
CstiimService是你自己定义的类吗?
CstiimSQL.DeleteDataCost(service, Transaction, strCost);
这里的service, Transaction都是做为参数传过来的吗?传递service这个参数是什么意思?还有这个事务处理在什么地方结束呢?
public void DataOperate1(DbTransaction tran, ...){...} //在函数体内不要提交事务
public void DataOperate2(DbTransaction tran, ...){...} //在函数体内不要提交事务public void Do()
{
DbConnection conn= new SqlConnection("...");
try{
conn.Open();
DbTransaction tran = conn.BeginTransaction();
DataOperate1(tran,...);
DataOperate2(tran, ...);
tran.Commit();
}
catch
{
if(tran != null){tran.RollBack();}
}
}
在这个函数体内应该怎样处理事务呢?还是在函数体内不做任何处理,只需要将事务做为参数传过来就可以了吗?
还有,必须用同一个数据库连接吗?
我在写程序的时候,用了类封装,将所有对数据库的操作封装在一个类里;在程序代码里调用这个类执行对数据库的操作;这样就导致我很难用一个数据库连接来做所有的数据库操作。
1.Connection.BeginTrans()
2.using System.Transactions;
例如
DbConnection conn= new SqlConnection("...");
try{
conn.Open();
DbTransaction tran = conn.BeginTransaction();
bool success=false;
if(bid.do1(tran))
if(bid.do2(tran))
if(bid.do3(tran))
if(bid.do4(tran))
if(bid.do5(tran))
success=true;
if(success)
tran.Commit();
else
tran.RollBack();
}
catch
{
tran.RollBack();
}
finally
{
最好检测一下连接是否关闭
}
至于事务传递的具体实现,可以参考一下著名的SqlHelper类中的底层实现。
下面的示例说明 TransactionScope 类的简单用法。Visual Basic 复制代码
Using scope As TransactionScope = New TransactionScope() 'Create and open the SQL connection. The work done on this connection will be a part of the transaction created by the TransactionScope
Dim myConnection As New SqlConnection("server=(local)\SQLExpress;Integrated Security=SSPI;database=northwind")
Dim myCommand As New SqlCommand()
myConnection.Open()
myCommand.Connection = myConnection 'Restore database to near it's original condition so sample will work correctly.
myCommand.CommandText = "DELETE FROM Region WHERE (RegionID = 100) OR (RegionID = 101)"
myCommand.ExecuteNonQuery() 'Insert the first record.
myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (100, 'MidWestern')"
myCommand.ExecuteNonQuery() 'Insert the second record.
myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (101, 'MidEastern')"
myCommand.ExecuteNonQuery() myConnection.Close() 'Call complete on the TransactionScope or not based on input
Dim c As ConsoleKeyInfo
While (True) Console.Write("Complete the transaction scope? [Y|N] ")
c = Console.ReadKey()
Console.WriteLine()
If (c.KeyChar = "Y") Or (c.KeyChar = "y") Then
scope.Complete()
Exit While
ElseIf ((c.KeyChar = "N") Or (c.KeyChar = "n")) Then
Exit While
End If
End WhileEnd Using
C# 复制代码
using (TransactionScope ts = new TransactionScope())
{
//Create and open the SQL connection. The work done on this connection will be a part of the transaction created by the TransactionScope
SqlConnection myConnection = new SqlConnection("server=(local)\\SQLExpress;Integrated Security=SSPI;database=northwind");
SqlCommand myCommand = new SqlCommand();
myConnection.Open();
myCommand.Connection = myConnection; //Restore database to near it's original condition so sample will work correctly.
myCommand.CommandText = "DELETE FROM Region WHERE (RegionID = 100) OR (RegionID = 101)";
myCommand.ExecuteNonQuery(); //Insert the first record.
myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (100, 'MidWestern')";
myCommand.ExecuteNonQuery(); //Insert the second record.
myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (101, 'MidEastern')";
myCommand.ExecuteNonQuery(); myConnection.Close(); //Call complete on the TransactionScope or not based on input
ConsoleKeyInfo c;
while (true)
{
Console.Write("Complete the transaction scope? [Y|N] ");
c = Console.ReadKey();
Console.WriteLine(); if ((c.KeyChar == 'Y') || (c.KeyChar == 'y'))
{
// Commit the transaction
ts.Complete();
break;
}
else if ((c.KeyChar == 'N') || (c.KeyChar == 'n'))
{
break;
}
} 您创建了新的 TransactionScope 对象后,即开始事务范围。 如代码示例中所示,建议您使用 using 语句创建范围。C# 和 Visual Basic 中都提供 using 语句,其工作方式类似于 try...finally 块,可确保正确处理范围。 实例化 TransactionScope 时,事务管理器会确定要参与的事务。确定之后,该范围将始终参与此事务。这个决定基于两个因素:环境事务是否存在以及构造函数中 TransactionScopeOption 参数的值。环境事务是在其中执行代码的事务。您可以通过调用 Transaction 类的静态 Current 属性,获取对环境事务的引用。有关如何使用此参数的更多信息,请参见本主题的使用 TransactionScopeOption 管理事务流一节。完成事务范围
在您的应用程序完成了它要在某一事务中执行的所有工作后,应该只调用 Complete 方法一次,以便通知事务管理器它可以提交该事务。强烈建议将对 Complete 的调用作为最后一条语句放置在 using 块中。 如果调用此方法失败,则会中止该事务,因为事务管理器会将此解释为一个系统故障,或者与在事务范围内引发的异常等效的故障。但是,调用此方法并不确保将提交该事务。这只是一个向事务管理器通知您的状态的方法。在调用 Complete 方法后,您将无法再通过 Current 属性访问环境事务,这样做将会引发异常。 如果 TransactionScope 对象最初创建了该事务,则通过事务管理器提交该事务的实际工作将在 using 块中的最后一行代码后发生。如果它没有创建该事务,则只要 CommittableTransaction 对象的所有者调用了 Commit,就发生提交。在该时刻,事务管理器将基于对 TransactionScope 对象是否已调用了 Complete 方法,调用资源管理器并通知它们提交或回滚。using 语句确保调用 TransactionScope 对象的 Dispose 方法,即使发生异常也是如此。Dispose 方法标记事务范围的结束。在调用此方法后发生的异常不会影响该事务。该方法还将环境事务还原为其以前的状态。 如果范围创建该事务,则引发 TransactionAbortedException,并中止该事务。如果事务管理器无法达成提交决定,则引发 TransactionIndoubtException。如果提交该事务,则不会引发任何异常。回滚事务
要回滚某个事务,不应在该事务范围内调用 Complete 方法。例如,您可以在该范围内引发异常。该异常所参与的事务将被回滚。
TransactionScope事务范围内也可以使用不同的数据库联接的。
using System.Data.SqlClient;namespace CWMS.DataStore
{
public partial class Bid
{ public void do1()
{
System.Data.SqlClient.SqlConnection myConn = new System.Data.SqlClient.SqlConnection(Form1.myConnString);
System.Data.SqlClient.SqlCommand myCmd = new System.Data.SqlClient.SqlCommand();
System.Data.SqlClient.SqlDataAdapter myDA = new System.Data.SqlClient.SqlDataAdapter();
System.Data.DataSet myDS = new System.Data.DataSet();
DataTable myTable = new DataTable();
try
{
myConn.Open();
SQL操作1
}
catch
{
}
finaly
{
mycon.close();
}
}
do2(),do3(),do4(),do5(),这几个函数都是一样处理的。
}
}
下面我进行了一下修改:
namespace CWMS.DataStore
{
public partial class Bid
{
System.Data.SqlClient.SqlConnection myConn = new System.Data.SqlClient.SqlConnection(Form1.myConnString);
System.Data.SqlClient.SqlCommand myCmd = new System.Data.SqlClient.SqlCommand();
System.Data.SqlClient.SqlDataAdapter myDA = new System.Data.SqlClient.SqlDataAdapter();
System.Data.DataSet myDS = new System.Data.DataSet();
DataTable myTable = new DataTable();
SqlTransaction mytran;
mytran = myConn.BeginTransaction(); public void do1(mytran)
{
myCmd.Transaction = mytran;
myCmd.Connection = myConn;
SQL操作1
}
public void do2(mytran)
{
myCmd.Transaction = mytran;
myCmd.Connection = myConn;
SQL操作2
}
……
public void dolast()
{
if(true)
{
mytran.commit();
}
else
{
mytran.rollback();
}
}
}
}
下面是我在程序中调用这类进行相关数据库操作:
private CWMS.DataStore.Bid bid = new CWMS.DataStore.Bid();
…… public override void Submit()
{
……
bid.do5();
if (成功)
{
…
bid.do1();
…
bid.do2();
…
bid.do3();
…
bid.do4();
…
bid.dolast();
}
}
那如果我这样改一下,是不是可行呢?
{
conn.Open();
SqlCommand cmd = new SqlCommand();
SqlTransaction tr = conn.BeginTransaction();
//应用程序通过在 SqlConnection 对象上调用 BeginTransaction 来创建 SqlTransaction 对象。
//SqlTransaction表示要在 SQL Server 数据库中处理的 Transact-SQL 事务。
cmd.Transaction = tr;
cmd.Connection = conn; try
{
cmd.CommandText = "insert into employees(employeeid, lastname, firstname) values(14, 'aaa', 'bbb')";
cmd.ExecuteNonQuery(); cmd.CommandText = "insert into employees(lastname, firstname) values ('eeeeee', 'bbb')";
cmd.ExecuteNonQuery(); tr.Commit();
}
catch
{
tr.Rollback();
}}
然后再用Transaction调用