LINQ to SQL分布式事务处理

newsopen

贡献于2011-01-05

字数:0 关键词: .NET开发 SQL

分布式事务处理 LINQ to SQL, C# 跨越两个或多个数据库的单个数据库引擎 实例中的事务实际上是分布式 事务。该实例对分布式事务进行内部管理;对于用户而言,其操作就像本 地事务一样。 2009 sunny www.sunnycoder.cn 2009-12-24 2 / 8 本文内容  T-SQL 中的事务处理  ADO.NET 中的事务处理  LINQ to SQL 中的隐式事务处理  分布式事务处理 释义 分布式事务跨越两个或多个称为资源管理器的服务器。称为事务管理器的服务器组件必须在资源管理 器之间协调事务管理。如果分布式事务由 Microsoft 分布式事务处理协调器 (MS DTC) 之类的事务管理器 或其他支持 X/Open XA 分布式事务处理规范的事务管理器来协调,则每个 SQL Server Database Engine 实例都可以作为资源管理器来运行。有关详细信息,请参阅 MS DTC 文档。 跨越两个或多个数据库的单个数据库引擎 实例中的事务实际上是分布式事务。该实例对分布式事务 进行内部管理;对于用户而言,其操作就像本地事务一样。 对于应用程序而言,管理分布式事务很像管理本地事务。当事务结束时,应用程序会请求提交或回滚 事务。不同的是,分布式提交必须由事务管理器管理,以尽量避免出现因网络故障而导致事务由某些资源 管理器成功提交,但由另一些资源管理器回滚的情况。通过分两个阶段(准备阶段和提交阶段)管理提交 进程可避免这种情况,这称为两阶段提交 (2PC)。 准备阶段: 当事务管理器收到提交请求时,它会向该事务涉及的所有资源管理器发送准备命令。然后,每个资源 管理器将尽力使该事务持久,并且所有保存该事务日志映象的缓冲区将被刷新到磁盘中。当每个资源管理 器完成准备阶段时,它会向事务管理器返回准备成功或准备失败的消息。 提交阶段: 如果事务管理器从所有资源管理器收到准备成功的消息,它将向每个资源管理器发送一个提交命令。 然后,资源管理器就可以完成提交。如果所有资源管理器都报告提交成功,那么事务管理器就会向应用程 序发送一个成功通知。如果任一资源管理器报告准备失败,那么事务管理器将向每个资源管理器发送一个 回滚命令,并向应用程序表明提交失败。 数据库引擎 应用程序可以通过 Transact-SQL 或数据库 API 来管理分布式事务。 测试数据库 数据库名称:test 数据库表名称:Users 表结构: 列名 数据类型 允许为空 注释 UsersID int false 主键,自增标识 UserName varchar(50) false 用户名,唯一约束 Password varchar(50) false 3 / 8 在文章开始之前,我们首先要明确:事务是单个的工作单元。如果某一事务成功,则在该事务中进行 的所有数据修改均会提交,成为数据库中的永久组成部分。如果事务遇到错误且必须取消或回滚,则所有 数据修改均被清除。也就是说,当我们使用 SQL Server 的查询分析器中执行了了一行代码“insert into t values(x, y, z)”,数据成功地添加到了数据库中,但这并不是说这里没有使用事务处理,而是系统帮我们将 常用的操作自动地做了一些处理(我猜想应该是代理模式)。 SQL Server 数据库为我们提供了以下几种事务模式: 自动提交事务 每条单独的语句都是一个事务。 显式事务 每个事务均以 BEGIN TRANSACTION 语句显式开始,以 COMMIT 或 ROLLBACK 语句显 式结束。 隐式事务 在前一个事务完成时新事务隐式启动,但每个事务仍以 COMMIT 或 ROLLBACK 语句显式 完成。 批处理级事务 只能应用于多个活动结果集 (MARS),在 MARS 会话中启动的 Transact-SQL 显式或 隐式事务变为批处理级事务。当批处理完成时没有提交或回滚的批处理级事务自动由 SQL Server 进行回 滚。 第一节 T-SQL 中的事务处理 下面的代码演示了如何使用 T-SQL 语句创建一个显式事务: --开启一个事务 begin transaction --使用try…catch结构捕获异常 begin try --插入两条数据(相同的用户名) INSERT INTO [test].[dbo].[Users] ([UserName] ,[Password]) VALUES ('张三','123') INSERT INTO [test].[dbo].[Users] ([UserName] ,[Password]) VALUES ('张三','456') commit transaction --提交事务 end try --如果出现了异常,进入catch代码段 begin catch rollback transaction --回滚事务 select ERROR_MESSAGE() as [Message] --输出错误信息 end catch 4 / 8 以上代码执行后会失败,错误信息为:“违反了 UNIQUE KEY 约束 'UQ_Users_UserName'。不能在对 象 'dbo.Users' 中插入重复键”。 需要注意的是,在 SQL Server 中,如果没有显式声明事务,那么系统将分配隐性事务。当数据库引擎 实例首次执行下列任何语句时,都会自动启动一个事务: ALTER TABLE INSERT CREATE OPEN DELETE REVOKE DROP SELECT FETCH TRUNCATE TABLE GRANT UPDATE 第二节 ADO.NET 中的事务处理 以下代码演示了如何使用 SqlClient 组件来创建一个显式事务: using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); SqlCommand command = connection.CreateCommand(); SqlTransaction transaction; // 启动一个本地事务 transaction = connection.BeginTransaction(); // 在启动一个本地事务之前,需要为Command对象指派Connection对象与Transaction对象 command.Connection = connection; command.Transaction = transaction; try { command.CommandText = "INSERT INTO [test].[dbo].[Users] ([UserName] ,[Password]) VALUES ('张三','123')"; command.ExecuteNonQuery(); command.CommandText = "INSERT INTO [test].[dbo].[Users] ([UserName] ,[Password]) VALUES ('张三','456')"; command.ExecuteNonQuery(); // 尝试提交事务 transaction.Commit(); 5 / 8 Console.WriteLine("所有的记录已经提交至数据库"); } catch (Exception ex) { Console.WriteLine("引发的异常类型为: {0}", ex.GetType()); Console.WriteLine("异常信息: {0}", ex.Message); // 尝试回滚事务 try { transaction.Rollback(); } catch (Exception ex2) { // 此catch块将处理任何可能发生在服务器上的,导致回滚失败的错误。如连接已关闭。 Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType()); Console.WriteLine(" Message: {0}", ex2.Message); } } } 以上代码执行后会失败,错误信息为:“违反了 UNIQUE KEY 约束 'UQ_Users_UserName'。不能在对 象 'dbo.Users' 中插入重复键”。 第三节 LINQ to SQL 中的隐式事务处理 下面的代码演示了如何使用 LINQ to SQL 技术进行事务处理: using (TestDataContext db = new TestDataContext()) { // 在控制台中输出T-SQL语句 db.Log = Console.Out; //创建两个Users对象(注意,用户名是相同的) Users u1 = new Users() { UserName = "张三", Password = "123" }; Users u2 = new Users() { UserName = "张三", Password = "456" }; try { db.Users.InsertAllOnSubmit(new Users[] { u1, u2 }); 6 / 8 // 尝试提交事务 db.SubmitChanges(); Console.WriteLine("所有的记录已经提交至数据库"); } catch (Exception ex) { Console.WriteLine("引发的异常类型为: {0}", ex.GetType()); Console.WriteLine("异常信息: {0}", ex.Message); } } 以上代码执行后会失败,错误信息为:“违反了 UNIQUE KEY 约束 'UQ_Users_UserName'。不能在对 象 'dbo.Users' 中插入重复键”。 在代码中,当调用 SubmitChanges 时,LINQ to SQL 会检查此调用是否在 Transaction 的作用域内或 者 Transaction 属性 (IDbTransaction) 是否设置为由用户启动的本地事务。如果这两个事务它均未找到, 则 LINQ to SQL 启动本地事务 (IDbTransaction),并使用此事务执行所生成的 SQL 命令。当所有 SQL 命 令均已成功执行完毕时,LINQ to SQL 提交本地事务并返回。这就是 LINQ to SQL 的“隐式事务”。 第四节 分布式事务处理 本文进行到这里,将使用一个问题引入标题内容“分布式事务处理”是怎样的一种操作:如何将两个 DataContext 对象的提交过程当做一个事务来处理? 可能你对这个问题不是太明白,下面的代码演示了这个问题是一个怎样的过程: //创建两个TestDataContext对象 TestDataContext db1 = new TestDataContext(); TestDataContext db2 = new TestDataContext(); //创建两个Users对象(注意,用户名是相同的) Users u1 = new Users() { UserName = "张三", Password = "123" }; Users u2 = new Users() { UserName = "张三", Password = "456" }; try { //将两个Users对象分别加入到不同的TestDataContext对象中 db1.Users.InsertOnSubmit(u1); db2.Users.InsertOnSubmit(u2); // 尝试提交事务 7 / 8 db1.SubmitChanges(); db2.SubmitChanges(); Console.WriteLine("所有的记录已经提交至数据库"); } catch (Exception ex) { Console.WriteLine("引发的异常类型为: {0}", ex.GetType()); Console.WriteLine("异常信息: {0}", ex.Message); } 以上代码执行后同样会失败,错误信息为:“违反了 UNIQUE KEY 约束 'UQ_Users_UserName'。不能 在对象 'dbo.Users' 中插入重复键”。但是明显的,数据库中将成功添加一条数据——db1.SubmitChanges() 执行时提交的数据。 那么,如何才能让这个过程符合我们的要求呢?以下代码演示了这一点: //创建两个TestDataContext对象 TestDataContext db1 = new TestDataContext(); TestDataContext db2 = new TestDataContext(); //创建两个Users对象(注意,用户名是相同的) Users u1 = new Users() { UserName = "张三", Password = "123" }; Users u2 = new Users() { UserName = "张三", Password = "456" }; //将两个Users对象分别加入到不同的TestDataContext对象中 db1.Users.InsertOnSubmit(u1); db2.Users.InsertOnSubmit(u2); // 使用TransactionScope对象,使代码块成为事务性代码。 using (TransactionScope rs = new TransactionScope()) { try { // 尝试提交事务 db1.SubmitChanges(); db2.SubmitChanges(); rs.Complete(); Console.WriteLine("所有的记录已经提交至数据库"); } catch (Exception ex) { Console.WriteLine("引发的异常类型为: {0}", ex.GetType()); 8 / 8 Console.WriteLine("异常信息: {0}", ex.Message); } } 当决定使用 SQL Server 分布式事务处理模式时,必须启动名称为 Distributed Transaction Coordinator 的 Windows 服务项,你可以在服务管理器中启动它(开始菜单-运行- services.msc),也可以在命令提示符 中直接启动它(net start msdtc)。 这时,包含在 TransactionScope 对象代码块中的两个 DataContext 对象,其执行 SubmitChanges 方法 时将处于事务之中,如果此时出现了异常,那么之前的所有操作将会回滚。也就是说如果在事务范围中(即 从初始化 TransactionScope 对象到调用其 Dispose 方法之间)发生异常了异常,事务都将结束,并回滚 至初始状态。这意味着,即使执行了 Complete 方法,在这之后发生的异常也将导致事务失败。所以,尽 量不要在执行 Complete 方法与 Dispose 方法之间,加入可能会导致事务失败的代码。 如果 TransactionScope 对象成功执行了 Complete 方法,那么所有数据将提交至数据库中。需要注意 的是,如果在 TransactionScope 对象代码块中未执行 TransactionScope 对象的 Complete 方法,那么事务 也将终止并回滚至初始状态。 同样的,TransactionScope 对象代码块也适用于其他代码级数据库访问,例如将多个 SqlConnection 对象的提交过程放在同一个 TransactionScope 对象代码块中,就可以方便地控制事务的执行方式。

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享文档获得金币 ] 0 人已下载

下载文档

相关文档