使用 IBM Data Server Provider for .NET 开发异构数据库应用程序

使用分布式事务和 IBM Data Server Provider for .NET

跨异构数据库工作的事务逻辑需要使用全局事务。IBM Data Server Provider for .NET 支持 System.Transactions 名称空间的分布式事务模型,该模型能够创建和管理全局事务。本文中的代码示例将演示如何使用 IBM Data Server Provider for .NET 来创建和管理 IBM 数据服务器的全局事务。

Kanchana Padmanabhan, 咨询软件工程师, IBM

作者照片Kanchana Padmanabhan 在过去 11 年一直致力于 IBM Data Servers 的客户端连接工具方面的工作。她是 IBM Data Servers Providers for .NET 团队的技术领导人。



2012 年 3 月 12 日

免费下载:IBM® DB2® Express-C 9.7.2 免费版 或者 DB2® 9.7 for Linux®, UNIX®, and Windows® 试用版
下载更多的 IBM 软件试用版,并加入 IBM 软件下载与技术交流群组,参与在线交流。

简介

企业系统通常由异构数据库组成。事务逻辑需要建立多个连接,以连接到一个或多个这些异构数据库,这是一种很常见的情况。为了保持事务操作的原子性,应用程序需要使用分布式事务模型。在 .NET 框架中实施该模型可简化其使用。本文中的代码示例将演示如何使用 IBM Data Server Provider for .NET 在 IBM 数据服务器上创建和管理全局事务。

在 .NET 框架中的 System.Transactions 名称空间包括创建和管理全局事务的功能,能够在一个或多个数据库上跨多个连接进行操作。Microsoft Distributed Transaction Coordinator 服务 (MSDTC) 可协调这些全局事务。IBM Data Server Provider for .NET 扩展了 System.Transactions 名称空间的功能,以支持针对 IBM 数据库服务器的全局事务。有关 DB2® .NET 提供程序所支持的服务器清单,请参照 DB2 .NET 提供程序的在线帮助(参见 参考资料)。

本文介绍了如何使用 IBM Data Server Provider for .NET 创建和管理全局事务。代码样例显示了登记连接参与全局事务的不同方式。样例均使用 C++ 编写,并使用 IBM Data Server Provider for .NET。跨两个数据库执行事务逻辑:一个名为 salesDB,拥有客户及其销售订单的信息;而另一个名为 invDB,包含有关产品及其库存的信息。所执行的事务是处理订单,涉及到在 sales 表中执行插入操作和更新 products 表中的可用数量。

开始之前

DB2 .NET Provider 使用 XA 协议来实现对分布式事务模型的支持。因此 MSDTC 应被配置为启用 XA 事务。有关该配置的详细信息,请参阅相应的 Microsoft 文档(参见 参考资料)。

DB2 .NET Provider 仅支持 TCP/IP 连接上的全局事务。如果您使用的是本地数据库服务器,请确保您已将实例配置为 TCP/IP 通信(参见 参考资料)。

System.Transactions 名称空间是由类组成的,提供分布式事务模型的功能。将 清单 1 的代码添加到 System.Transactions.dll 程序集中的 System.Transactions 名称空间,从而在您的应用程序中使用该名称空间。

清单 1. 使用名称空间
using System.Transactions;

您的项目应该添加对 System.Transactions.dll 程序集的引用。


使用自动登记方法

自动登记是执行全局事务的默认和推荐方法。步骤如下:

  1. 创建一个环境事务。
  2. 建立数据库连接。
  3. 在每个连接上工作。
  4. 完成事务。

清单 2 显示完成自动登记步骤的代码示例。

清单 2. 自动登记的代码示例
using (TransactionScope ts = new TransactionScope())
    { 
        DB2Connection conn1 = new DB2Connection(
                                  "Database=salesdb; Server=salesServer:446;" +
                                  "UserID=user1; Password=xxxxxx"); 
        conn1.Open(); 

        DB2Command cmd1 = conn1.CreateCommand(); 
        cmd1.CommandText =                                              //do a sale
            "INSERT INTO sales (custID, prdID, quantity) VALUES(100, 10001, 50)"; 
        cmd1.ExecuteNonQuery(); 

        DB2Connection conn2 = new DB2Connection(
                                   "Database=invDB; Server=invServer:50000;" +
                                   "UserID=user2; Password=xxxxxx"); 
        conn2.Open(); 

        DB2Command cmd2 = conn2.CreateCommand(); 
        cmd2.CommandText =                                             //reduce inventory
            "UPDATE products SET quantity = quantity - 50 WHERE prdid = 10001"; 
        cmd2.ExecuteNonQuery(); 

        conn1.Close();
        conn2.Close();

        ts.Complete();
    }

清单 2 中,当 TransactionScope 对象被实例化时,会创建环境事务。环境事务是由非零值的 Transaction.Current 表示。在环境事务范围内打开的连接(在此示例中为 using 块)将在全局事务中自动登记。对 Complete 的调用将确认环境事务。如果因任何原因(最常见的是,一个异常)没有调用 Complete 方法,将回滚全局事务。


使用手动登记方法

手动登记方法没有环境事务。应用程序显式创建一个事务对象,并用它来手动登记事务中的连接。您必须在连接字符串中设置 enlist=false 以执行手动登记。在此方法中所涉及的步骤如下:

  1. 创建一个事务对象。
  2. 用事务对象登记连接。
  3. 在已登记的连接上工作。
  4. 完成事务。

清单 3 显示完成手动登记步骤的代码示例。

清单 3. 手动登记的代码示例
DB2Connection conn1 = new DB2Connection(
                                "Database=salesdb; Server=salesServer:446;" + 
                                "UserID=user1; Password=xxxxxx; enlist=false"); 
    conn1.Open(); 
    DB2Connection conn2 = new DB2Connection(
                                "Database=invDB; Server=invServer:50000;" + 
                                "UserID=user2; Password=xxxxxx; enlist=false"); 
    conn2.Open(); 
    CommittableTransaction cmtxn = new CommittableTransaction(); 
    try 
    { 
        conn1.EnlistTransaction(cmtxn); 

        DB2Command cmd1 = conn1.CreateCommand(); 
        cmd1.CommandText =                                              //do a sale
            "INSERT INTO sales (custID, prdID, quantity) VALUES(100, 10001, 50)"; 
        cmd1.ExecuteNonQuery(); 

        conn2.EnlistTransaction(cmtxn); 
        DB2Command cmd2 = conn2.CreateCommand(); 
        cmd2.CommandText =                                             //reduce inventory
            "UPDATE products SET quantity = quantity - 50 WHERE prdid = 10001"; 
        cmd2.ExecuteNonQuery(); 

        cmtxn.Commit(); 
    } 
    catch (Exception) 
    { 
        cmtxn.Rollback(); 
    }

清单 3 中,请注意,创建事务后不需要打开连接。这使应用程序能够在其代码中实现更多模块化。这种方法涉及到更多的编码工作,但它也有一些优势,这将在下一节介绍。


在全局和本地事务之间切换

使用手动登记方法时,应用程序可以使用连接在全局事务外部工作。应用程序在登记之前或取消登记之后都可以使用连接来执行本地事务。清单 4 演示如何在全局和本地事务之间进行切换。

清单 4. 在全局和本地事务之间进行切换的代码样例
DB2Connection conn1 = new DB2Connection(
                                "Database=salesdb; Server=salesServer:446;" + 
                                "UserID=user1; Password=xxxxxx; enlist=false"); 
    conn1.Open(); 
    DB2Connection conn2 = new DB2Connection(
                                "Database=invDB; Server=invServer:50000;" + 
                                "UserID=user2; Password=xxxxxx; enlist=false"); 
    conn2.Open(); 
    CommittableTransaction cmtxn = new CommittableTransaction(); 
    try 
    { 
        conn1.EnlistTransaction(cmtxn); 

        DB2Command cmd1 = conn1.CreateCommand(); 
        cmd1.CommandText =                                              //do a sale
            "INSERT INTO sales (custID, prdID, quantity) VALUES(100, 10001, 50)"; 
        cmd1.ExecuteNonQuery(); 

        conn2.EnlistTransaction(cmtxn); 
        DB2Command cmd2 = conn2.CreateCommand(); 
        cmd2.CommandText =                                             //reduce inventory
            "UPDATE products SET quantity = quantity - 50 WHERE prdid = 10001"; 
        cmd2.ExecuteNonQuery(); 

        cmtxn.Commit(); 
    } 
    catch (Exception) 
    { 
        cmtxn.Rollback(); 
    } 
    conn2.EnlistTransaction(null);
    //call stored procedure place more order to replenish inventory
    cmd2.CommandText = "CALL ReplenishStock(?)"; 
    cmd2.Parameters.Add("@prdid", 10001");
    cmd2.ExecuteNonQuery();
   
    conn1.EnlistTransaction(null);
    DB2Transaction txn = conn1.BeginTransaction();
    cmd1.Transaction = txn;
    try
    {
        cmd1.CommandText =           //increase the count of sales given by this customer
             "UPDATE customers SET salesCount = salesCount + 1 WHERE custID = 100"; 
        cmd1.ExecuteNonQuery();
        cmd1.CommandText =           //make him eligible for 10% discount for future
            "INSERT INTO discounts (custID, prdid, discount) VALUES (100, 10001, 10)"; 
        cmd1.ExecuteNonQeury();
        txn.Commit();
    }
    catch
    {
        txn.Rollback();
    }

清单 4 中,conn2 在需要调用 ReplenishStock 存储的过程时,并不需要事务开销。应用程序调用带有空事务 ID 的 EnlistTransaction 方法,以取消登记连接。conn1 的工作是更新 customers 表并在 discounts 表中插入,这些工作需要在事务内完成。但由于操作都在同一连接上,使用本地事务就足够了。应用程序取消登记连接,并通过调用 BeginTransaction 方法创建一个本地事务。

请注意,清单 4 的代码需要 APAR IC77576 补丁。该补丁在 DB2 for z/OS 9.7 Fixpack 5 或其更高版本的 IBM Data Server Client Package 中有提供。如果没有 APAR 补丁,您会收到一个 DB2Exception 异常。有关的更多详细信息,请参考 APAR。


理解池

实现分布式事务模型要求打开连接池。池为默认情况打开状态。显式地关闭池将导致在应用程序运行期间出现 TransactionAbortedException 异常。


对 DB2 for z/OS 运行全局事务

下面的数据库服务器仅在使用 DB2 Connect 网关进行连接时支持全局事务。

  • DB2 Universal Database for z/OS, Version 8
  • DB2 Universal Database for z/OS, Version 9,没有 APAR PK69659

控制连接是否直接执行全局事务

DB2 for z/OS, Version 10,以及带有 APAR PK69659 的 DB2 for z/OS, Version 9,引入了在没有 DB2 Connect 网关的情况下执行全局事务的支持。db2dsdriver.cfg 文件中的 enableDirectXA 设置可以用于控制连接是否在数据库服务器上直接执行全局事务,还是使用 DB2 Connect 网关。有关 enableDirectXA 设置的更多信息,请参阅 参考资料


使用 testconn 实用工具

testconn 实用工具可以用于验证 IBM Data Server Driver Package 是否已安装以及 MSDTC 是否配置正确,以对 IBM 数据服务器执行全局事务。-dtc 开关应该用于验证。有关 testconn 工具的信息,请参阅 参考资料。清单 5 显示了 testconn 实用工具的输出,报告一个错误,并针对该问题建议可能的解决方案。

清单 5. 使用 testconn 实用工具进行验证
Step 6: Creating XA connection
        DB2TransactionScope: Failed to open connection to database!
IBM.Data.DB2.DB2Exception: ERROR [58005] [IBM][DB2/NT] SQL0998N  Error occurred
during transaction or heuristic processing.  Reason Code = "16". Subcode = "2-80
04D026".
   at IBM.Data.DB2.DB2Connection.Open()
   at TestConn.DB2TransactionScope.Open(String connstr)

        Looking for XADLL key for E:\SQLLIB\bin\db2app.dll
           Correct entry found
           Check if XA Transactions are enabled:
              Windows 7, 2008, Vista -
                Component Services, Computers, My Computer,
                Distributed Transaction Coordinator, Local DTC
                Properties, Security tab, Enable XA Transactions
              Other windows versions -
                Component Services, Computers, My computer, Properties
                MSDTC tab, Security configuration, Enable XA Transactions

疑难解答

表 1 列出了应用程序在执行全局事务时经常遇到的错误及其可能的解决方案。

表 1. 错误、场景和可能的纠正操作
操作错误的详细信息纠正操作
项目编译无法解析 CommittableTransaction 或 TransactionScopeSystem.Transactions.dlli 添加到项目引用。 将 using System.Transactions; 包括在代码中。
DB2Connection.Open在事务或启发式处理过程中发生 DB2Exception, SQLState - 58005, SQL0998N Error。Reason Code = "16"。 Subcode = "2-8004D026"。确保 MSDTC 安全配置选项卡中的 Enable XA transaction 框被选中。
在事务或启发式处理过程中发生 DB2Exception, SQLState - 58005, SQL0998N Error。Reason Code = "16"。 Subcode = "2-8004D025"。验证 XADLL 条目是否有效。
DB2Exception, SQLState - 580051. 如果您连接到直接支持 XA 的服务器,并且如果您使用 IBM Data Server Client Package(不是 IBM Data Server Driver Package),请将 db2dsdriver.cfg 文件中的 enableDirectXA 设置为 true。2. 如果您在使用 IBM Data Server Driver Package,请确保没有禁用 enableDirectXA。3. 如果您连接到不能直接支持 XA 的服务器,请通过 DB2 Connect 网关进行连接。
完成全局事务TransactionAbortedException, Message - The transaction has aborted.请确保您已在连接字符串中设置了 Pooling=false。在参与全局事务的连接上必须启用池。
DB2Connection.BeginTransactionDB2Exception, SQLState - HY011, Message CLI0126E Operation invalid at this time.因为该连接已在全局事务中登记,所以不允许调用 BeginTransaction。
DB2Connection.EnlistTransactionSystem.InvalidOperationException, Message - Connection currently has transaction enlisted.因为该连接已自动登记,所以不允许调用 EnlistTransaction。

结束语

本文已解释,通过在 .NET 中实现分布式事务模型,可简化 .NET 应用程序中的模型使用。除了创建一个事务对象并编写逻辑来完成事务之外,不管是否存在全局事务,您的应用程序看起来都是一样的。这是因为创建和管理全局事务的所有复杂逻辑都在 IBM Data Server Provider for .NET 和 .NET Framework 中进行处理。我们留给您的是一个简单但功能强大的应用程序,足以满足您的企业的需求。

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Information Management
ArticleID=801536
ArticleTitle=使用 IBM Data Server Provider for .NET 开发异构数据库应用程序
publish-date=03122012