在本人的 “ .NET简谈事务本质论”一文中我们从整体上了解了事务模型,在我们脑子里能有一个全局的事务处理结构,消除对数据库事务的依赖理解,重新认识事务编程模型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
insert
into
test
values
(
'222'
)
--我们在表test中插入一条记录
go
begin
transaction
tr
begin
try
begin
insert
into
test2
values
(
'111'
)
insert
into
test
values
(
'222'
)
--该行插入会导致主键冲突,也就是我们要的效果
end
commit
transaction
tr
end
try
begin
catch
print
'事务执行错误!'
print error_number()
rollback
transaction
tr
end
catch
|
例2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public
class
Test
{
SqlConnection conn =
new
SqlConnection(
"data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"
);
public
void
Add()
{
conn.Open();
SqlCommand command =
new
SqlCommand(
"insert into test2 values(111)"
, conn);
try
{
command.Transaction = conn.BeginTransaction();
command.ExecuteNonQuery();
command.CommandText =
"insert into test values(222)"
;
command.ExecuteNonQuery();
command.Transaction.Commit();
}
catch
(Exception err)
{
Console.WriteLine(err);
command.Transaction.Rollback();
}
}
}
|
这就是典型ADO.NET事务处理代码,其实和我们第一个例子中的T-SQL代码是差不多的,通过ADO.NET中的SqlConnection.BeginTransaction()获取到对底层ODBC中的数据库事务的引用,其实这里还没有真正的设计到.NET中的事务处理代码,这里只是对数据库管理系统的远程调用,通过远程处理的消息通讯进行事务处理远程化。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public
class
DisplayTransactioninfo
{
public
static
void
Display(System.Transactions.Transaction tr)
{
if
(tr !=
null
)
{
Console.WriteLine(
"Createtime:"
+ tr.TransactionInformation.CreationTime);
Console.WriteLine(
"Status:"
+ tr.TransactionInformation.Status);
Console.WriteLine(
"Local ID:"
+ tr.TransactionInformation.LocalIdentifier);
Console.WriteLine(
"Distributed ID:"
+ tr.TransactionInformation.DistributedIdentifier);
Console.WriteLine();
}
}
|
CommittableTransaction事务处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public
class
Test3
{
SqlConnection conn;
CommittableTransaction committran =
new
CommittableTransaction();
public
Test3()
{
conn =
new
SqlConnection(
"data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"
);
DisplayTransactioninfo.Display(committran);
}
public
void
Add3()
{
conn.Open();
conn.EnlistTransaction(committran);
//需要将本次的连接操作视为事务性的
SqlCommand command =
new
SqlCommand();
try
{
command.Connection = conn;
command.CommandText =
"insert into test2 values(111)"
;
command.ExecuteNonQuery();
command.CommandText =
"insert into test values(222)"
;
command.ExecuteNonQuery();
committran.Commit();
}
catch
(Exception err) { committran.Rollback();
//出现出错执行回滚操作}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
[System.Runtime.InteropServices.ComVisible(
true
)]
//COM+是在COM的基础上发展起来的,需要将.NET程序集中的类型公开为COM组件。
[System.EnterpriseServices.Transaction(TransactionOption.Required)]
//始终需要事务处理域
public
class
Test2 : ServicedComponent
{
public
Test2() { }
SqlConnection conn =
new
SqlConnection(
"data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"
);
[AutoComplete(
true
)]
//如果在方法的执行体类没有出现错误,那么将自动设置事务处理的结果
public
void
Add2()
{
conn.Open();
SqlCommand command =
new
SqlCommand();
try
{
command.Connection = conn;
command.CommandText =
"insert into test2 values(111)"
;
command.ExecuteNonQuery();
command.CommandText =
"insert into test values(222)"
;
command.ExecuteNonQuery();
}
catch
{ System.EnterpriseServices.ContextUtil.SetAbort(); }
}
}
|
DependentTransaction跨线程事务处理
我们在编写高并发量程序时,都会用到多线程来进行处理,让主线程能有时间来处理第一线的请求,然后将请求分发到各个子线程上进行后台的处理。我们来看一幅图:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
public
class
Test6
{
CommittableTransaction commit =
new
CommittableTransaction();
SqlConnection conn1 =
new
SqlConnection(
"data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"
);
public
Test6()
{
conn1.Open();
conn1.EnlistTransaction(commit);
}
public
void
Add6()
{
try
{
DisplayTransactioninfo.Display(commit);
SqlCommand command =
new
SqlCommand(
"insert into test2 values(111)"
, conn1);
command.ExecuteNonQuery();
Thread thread =
new
Thread(Test6.CommitThread);
thread.Start(commit.DependentClone(DependentCloneOption.BlockCommitUntilComplete));
commit.Commit();
}
catch
(Exception err) { commit.Rollback(); }
}
public
static
void
CommitThread(
object
co)
{
DependentTransaction commit = co
as
DependentTransaction;
SqlConnection conn2 =
new
SqlConnection(
"data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"
);
conn2.Open();
conn2.EnlistTransaction(commit
as
DependentTransaction);
DisplayTransactioninfo.Display(commit);
SqlCommand command =
new
SqlCommand(
"insert into test values(111)"
, conn2);
try
{
command.ExecuteNonQuery();
commit.Complete();
}
catch
(Exception err) { Console.WriteLine(err); commit.Rollback(); }
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public
class
Test4
{
SqlConnection conn1 =
new
SqlConnection(
"data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"
);
SqlConnection conn2 =
new
SqlConnection(
"data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"
);
CommittableTransaction committran =
new
CommittableTransaction();
public
Test4()
{
DisplayTransactioninfo.Display(committran);
conn1.Open();
conn1.EnlistTransaction(committran);
conn2.Open();
conn2.EnlistTransaction(committran);
DisplayTransactioninfo.Display(committran);
}
public
void
Add4()
{
try
{
SqlCommand command1 =
new
SqlCommand(
"insert into test2 values(111)"
, conn1);
command1.ExecuteNonQuery();
SqlCommand command2 =
new
SqlCommand(
"insert into test values(222)"
, conn2);
command2.ExecuteNonQuery();
}
catch
(Exception err) { Console.WriteLine(err); committran.Rollback(); }
}
}
|
1
2
3
4
5
6
7
8
|
//服务契约(ServiceContract):
[ServiceContract(SessionMode = SessionMode.Required)]
public
interface
IDistributedTransaction
{
[TransactionFlow(TransactionFlowOption.Allowed)]
[OperationContract]
void
Add();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//服务类1:
public
class
DistributedTransactionService1 : IDistributedTransaction
{
SqlConnection conn1 =
new
SqlConnection(
"data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"
);
#region IDistributedTransaction
[OperationBehavior(TransactionAutoComplete =
true
, TransactionScopeRequired =
true
)]
public
void
Add()
{
conn1.Open();
SqlCommand command =
new
SqlCommand(
"insert into test2 values(111)"
, conn1);
command.ExecuteNonQuery();
DataBaseOperation.DisplayTransactioninfo.Display(System.Transactions.Transaction.Current);
}
#endregion
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//服务类2:
public
class
DistributedTransactionService2 : IDistributedTransaction
{
SqlConnection conn2 =
new
SqlConnection(
"data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"
);
#region IDistributedTransaction
[OperationBehavior(TransactionAutoComplete =
true
, TransactionScopeRequired =
true
)]
public
void
Add()
{
conn2.Open();
SqlCommand command =
new
SqlCommand(
"insert into test values(222)"
, conn2);
try
{
DataBaseOperation.DisplayTransactioninfo.Display(System.Transactions.Transaction.Current);
command.ExecuteNonQuery();
}
catch
(Exception err) {
throw
err; }
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<service name=
"ServerConsole.Transaction.DistributedTransactionService1"
behaviorConfiguration=
"metadatabehaviors"
>
<host>
<baseAddresses>
</baseAddresses>
</host>
<endpoint address=
"DistributedTransactionService1"
binding=
"netTcpBinding"
bindingConfiguration=
"tranbinding"
contract=
"ServerConsole.Transaction.IDistributedTransaction"
></endpoint>
</service>
<service name=
"ServerConsole.Transaction.DistributedTransactionService2"
behaviorConfiguration=
"metadatabehaviors"
>
<host>
<baseAddresses>
</baseAddresses>
</host>
<endpoint address=
"DistributedTransactionService2"
binding=
"netTcpBinding"
bindingConfiguration=
"tranbinding"
contract=
"ServerConsole.Transaction.IDistributedTransaction"
></endpoint>
</service>
|
Binding配置:
1
2
3
4
5
6
7
|
<bindings>
<netTcpBinding>
<binding name=
"tranbinding"
transactionFlow=
"true"
transactionProtocol=
"WSAtomicTransactionOctober2004"
>
<reliableSession enabled=
"true"
ordered=
"true"
/>
</binding>
</netTcpBinding>
</bindings>
|
我们需要打开Binding的事务流传递。
客户端代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
DistributedTransactionClient.DistributedTransactionClient tranclient =
new
DistributedTransactionClient.DistributedTransactionClient();
DistributedTransaction2Client.DistributedTransactionClient tranclient2 =
new
DistributedTransaction2Client.DistributedTransactionClient();
using
(TransactionScope transcope =
new
TransactionScope())
{
try
{
Transaction.Current.TransactionCompleted +=
new
TransactionCompletedEventHandler(Current_TransactionCompleted);
tranclient.Add();
tranclient2.Add();
transcope.Complete();
}
catch
(Exception err) { Transaction.Current.Rollback(); }
}
static
void
Current_TransactionCompleted(
object
sender, TransactionEventArgs e)
{
if
(e.Transaction.TransactionInformation.Status ==
System.Transactions.TransactionStatus.Committed)
Console.WriteLine(e.Transaction.TransactionInformation.DistributedIdentifier);
}
|
客户端使用TransactionScope类来进行环境事务的设置,这样就很方便知道事务的执行范围,在TransactionScope里面我们可以通过Transaction.Current获取到当前上下文的事务对象,由于事务对象是存储在线程独立存储区里的,所以跨线程访问是没用的,通过依赖事务进行传递。[王清培版权所有,转载请给出署名]