您的位置:首页 > 数据库

使用TransactionScope实现单数据库连接事务操作

2009-03-12 08:23 666 查看
当应用程序需要在多个数据库中进行事务性操作的时候,使用TransactionScope类可以方便地实现应用程序的这一需求。只要对多个数据库的操作代码位于同一个事务范围内,即可实现多数据库连接的事务操作。

技术要点

本示例主要说明了如何在程序中使用TransactionScope实现多数据库连接事务操作,技术要点如下。

— 因为位于同一个事务范围内的不同的数据库操作,程序视为同一个事务,所以使用事务范围能够简便地实现多数据连接的事务操作。

— 在事务范围内应调用且仅仅调用一次Complete方法,当事务范围的Complete方法调用时,事务范围中的数据操作尝试提交,提交失败时自动回滚,如果在事务范围内未执行Complete方法,则导致事务范围在操作未提交的情况下结束。

实现步骤

(1)创建控制台应用程序项目,命名为“MultiDatabaseTransactionScope”。

(2)打开并编辑Program.cs文件,代码如下所示。

using System;

using System.Collections.Generic;

using System.Text;

using System.Transactions;

using System.Data;

using System.Data.SqlClient;

namespace MultiDatabaseTransactionScope

{

class Program

{

static void Main(string[] args)

{

//在创建的事务范围实例内运行代码

using (TransactionScope ts = new TransactionScope())

{

//连接数据库1的字符串

string ConnectionString1 = @"Data Source = localhost; Initial Catalog = Northwind; Integrated Security = SSPI;";

//创建数据库1连接类实例1

SqlConnection conn1 = new SqlConnection(ConnectionString1);

//创建数据库1命令类实例1

SqlCommand command1 = new SqlCommand(@"INSERT Shippers(CompanyName,Phone)

VALUES('Test Ship2','0000-0002')", conn1);

conn1.Open();//连接数据库1

command1.ExecuteNonQuery();//在数据库1上执行命令

Console.WriteLine("数据库1的命令已执行");

conn1.Close();//关闭数据库1

//连接数据库2的字符串

string ConnectionString2 = @"Data Source = localhost; Initial Catalog = pubs; Integrated Security = SSPI;";

//创建数据库2连接类实例2

SqlConnection conn2 = new SqlConnection(ConnectionString2);

//创建数据库2命令类实例2

SqlCommand command2 = new SqlCommand(@"INSERT Discounts(Discounttype,Discount) VALUES('Other',12)", conn2);

conn2.Open();//连接数据库2

command2.ExecuteNonQuery();//在数据库2上执行命令

Console.WriteLine("数据库2的命令已执行");

conn2.Close();//关闭数据库2

Console.Write("是否提交事务?(Y/N)");

if (Console.ReadKey(false).Key == ConsoleKey.Y)

{

ts.Complete();//提交事务

Console.WriteLine("");

Console.WriteLine("事务提交完成");

}

else

{

Console.WriteLine("取消事务提交");

}

}

}

}

}

(3)按F5键运行程序,运行结果如下所示。

数据库1的命令已执行

数据库2的命令已执行

是否提交事务?(Y/N)y

事务提交完成

使用2.0的新事务方式也有快一年了,刚开始时候遇到的一些使用疑点问题都在现在的项目中遇到,并解决,现在做一下总结:

一、 在TransactionScope中,如果不是必须要避免它启用DTC分布式事务,因为性能低下;而对于TransactionScope来说它是以连 接对象Connection做为识别单位的,也就是说即便是相同连接字符串ConnectionString的两个连接对象Connection在 TransactionScope也是会启用DTC分布式事务的,避免的方法就是在一个TransactionScope中使用一个唯一的连接对象 Connection。

二、在TransactionScope中默认的事务级别是Serializable,即在 事务过程中,完全性锁表。别的进程不能查询,修改,新增,删除。这样会导致效率大大降低,虽然数据完整性很高。通常我们不需要那么高的数据完整性。所以需 要修改默认的事务级别:

TransactionOptions option = new TransactionOptions();
option.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required, option))
所有的事务级别如下:
成员名称 说明
Chaos 无法改写隔离级别更高的事务中的挂起的更改。
ReadCommitted 不可以在事务期间读取可变数据,但是可以修改它。
ReadUncommitted 可以在事务期间读取和修改可变数据。
RepeatableRead 可以在事务期间读取可变数据,但是不可以修改。可以在事务期间添加新数据。
Serializable 可以在事务期间读取可变数据,但是不可以修改,也不可以添加任何新数据。
Snapshot 可以读取可变数据。在事务修改数据之前,它验证在它最初读取数据之后另一个事务是否更改过这些数据。如果数据已被更新,则会引发错误。这样使事务可获取先前提交的数据值。
在 尝试提升以此隔离级别创建的事务时,将引发一个 InvalidOperationException,并产生错误信息 “Transactions with IsolationLevel Snapshot cannot be promoted”(无法提升具 有 IsolationLevel 快照的事务)。

Unspecified 正在使用与指定隔离级别不同的隔离级别,但是无法确定该级别。如果设置了此值,则会引发异常。

使用TransactionScopeOption实现事务选项控制

TransactionScopeOption枚举是创建事务范围的重要选项,该枚举中包含三个成员,其中Required成员表示创建的范围需要 一个事务,如果已经存在外部的事务,就使用外部事务作为当前的事务,如果不存在外部事务,就创建一个新的事务,在事务范围的构造函数中,如果没有指定 TransactionScopeOption枚举的参数,默认使用Required创建事务范围。RequiresNew成员表示为当前范围创建一个新 的事务,而无论是否存在外部事务。Suppress成员表示取消当前范围的外部事务,范围内的所有操作在事务外部执行。

技术要点

本示例主要说明了如何在程序中使用TransactionScopeOption实现事务选项控制,技术要点如下。

使用RequiresNew成员创建的事务范围时,即使存在外部事务,当前范围也将创建新的事务,这样当前事务范围在调用Complete方法结束事务范围的时候,就执行了提交动作,而不是等到外部事务调用Complete方法时才进行提交。

使用Required成员创建的事务范围时,如果存在外部事务,就使用外部事务,这样当前事务范围在调用Complete方法结束事务范围的时候,实际上并不能进行执行提交动作。只有当外部的事务范围调用Complete方法结束时,才能执行提交动作。

实现步骤

(1)创建控制台应用程序项目,命名为“ControlTransactionScopeOption”。

(2)打开并编辑Program.cs文件,代码如下所示。

using System;

using System.Collections.Generic;

using System.Text;

using System.Data;

using System.Data.SqlClient;

using System.Transactions;

namespace ControlTransactionScopeOption

{

class Program

{

static void Main(string[] args)

{

//使用事务范围ts1

using (TransactionScope ts1 = new TransactionScope())

{

//连接数据库的字符串1

string ConnectionString1 = @"Data Source = localhost;

Initial Catalog = Northwind; Integrated Security = SSPI;";

//使用RequiresNew参数创建ts1的子事务subts1

using (TransactionScope subts1 = new TransactionScope

(TransactionScopeOption.RequiresNew))

{

//创建数据库连接类实例1

SqlConnection conn1 = new SqlConnection(ConnectionString1);

//将在数据库连接实例1上执行的命令

SqlCommand command1 = new SqlCommand(@"INSERT Shippers

(CompanyName, Phone) VALUES('Test Ship1','0000-0001')", conn1);

conn1.Open();

command1.ExecuteNonQuery();//执行数据库操作

conn1.Close();

subts1.Complete();//完成子事务1

}

//连接数据库的字符串2

string ConnectionString2 = @"Data Source = localhost;

Initial Catalog = Northwind; Integrated Security = SSPI;";

//使用Required参数创建ts1的子事务subts2

using (TransactionScope subts2 =new TransactionScope

(TransactionScopeOption.Required))

{

//创建数据库连接类实例2

SqlConnection conn2 = new SqlConnection(ConnectionString2);

//将在数据库连接实例2上执行的命令

SqlCommand command = new SqlCommand(@"INSERT Shippers

(CompanyName,Phone) VALUES('Test Ship2','0000-0002')",conn2);

conn2.Open();

command.ExecuteNonQuery();//执行数据库操作

conn2.Close();

subts2.Complete();//完成子事务

}

//完成ts1事务的语句,去掉注释后subts2的事务才能提交

//ts1.Complete();

//连接数据库的字符串3

string ConnectionString3 = @"Data Source = localhost;

Initial Catalog = Northwind; Integrated Security = SSPI;";

//创建数据库连接类实例3

SqlConnection conn3 = new SqlConnection(ConnectionString3);

//打开数据库连接

conn3.Open();

//读取数据库中Shippers表的数据

SqlDataReader dr = new SqlCommand("SELECT *

FROM Shippers", conn3).ExecuteReader();

while (dr.Read()) //循环显示数据

{

Console.WriteLine("{0} {1} {2}",

dr.GetInt32(0).ToString(),

dr.GetString(1),

dr.GetString(2));

}

dr.Close();//关闭SqlDataReader类实例

conn3.Close();//关闭数据库连接类实例

Console.ReadLine();

}

}

}

}
(3)按F5键运行程序,运行结果如下所示。
1 Speedy Express (503) 555-9831

2 United Package (503) 555-3199

3 Federal Shipping (503) 555-9931

4 Test Ship1 0000-0001
源程序解读

(1)本示例程序定义了一个外部的事务范围ts1,在该范围内分别使用TransactionScopeOption枚举的RequiresNew 成员和Required成员创建了两个事务范围。在这两个事务范围内,分别创建数据库连接,并执行SQL命令语句。然后在事务范围之外,查询并显示数据库 中的表记录,以检查事务的提交情况。本示例程序的流程图如图13.2所示。



498)this.style.width=498;" border=0>
图13.2 使用TransactionScopeOption实现事务选项控制的示例程序流程图
(2)根据程序运行结果显示,使用Required成员创建的事务范围中的操作未被提交,原因是Required成员创建的事务范围使用的是外部事务,在外部事务未提交时当前事务范围中的所有数据操作均未提交。

(3)除去本示例程序注释的外部事务范围ts1的调用Complete方法语句,将结束ts1事务,并将该事务范围内的所有数据操作提交,此时将提交Required成员创建的事务范围内的数据操作。

对MSDTC组件设置:
步骤:
在控制面板--->管理工具--->服务 中,开启Distributed Transaction Coordinator 服务。
a.控制面板->管理工具->组件服务->计算机->我的电脑->右键->属性
b.选择MSDTC页, 确认"使用本地协调器"
c.点击下方"安全配置"按钮
d.勾选: "允许网络DTC访问","允许远程客户端","允许入站","允许出站","不要求进行身份验证".
e.对于数据库服务器端, 可选择"要求对呼叫方验证"
f.勾选:"启用事务Internet协议(TIP)事务"。
g.在双方防火墙中增加MSDTC.exe例外
可用命令行: netsh firewall set allowedprogram %windir%\system32\msdtc.exe MSDTC enable

4、重启IIS服务器。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: