李天平的博客


天道酬勤
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

.NET开发中的事务处理大比拼 之 企业级服务COM+事务

Posted on 2009-06-17 20:49  李天平  阅读(2091)  评论(0编辑  收藏  举报

.NET Framework 依靠 MTS/COM+ 服务来支持自动事务处理。COM+ 使用 Microsoft Distributed Transaction CoordinatorDTC)作为事务管理器和事务协调器在分布式环境中运行事务。这样可使 .NET 应用程序运行跨多个资源结合不同操作(例如将定单插入SQL Server 数据库、将消息写入 Microsoft 消息队列(MSMQ)队列,以及从 Oracle 数据库检索数据)的事务。

要实现COM+事务处理的类则必须继承System.EnterpriseServices.ServicedComponent这些类需要是公共的,并且需要提供一个公共的默认的构造器。其实Web Service就是继承ServicedComponent,所以Web Service也支持COM+事务。要在类定义之前加属性[Transaction(TransactionOption.Required)]。类里面的每个方法都会运行在一个事务中。

定义一个COM+事务处理的类:

首先引用:using System.EnterpriseServices;

然后,继承ServicedComponent

[Transaction(TransactionOption.Required)]

public class OrderData : ServicedComponent

{

}

TransactionOption枚举类型支持5个值:DisabledNotSupportedRequiredRequiresNewSupported,如表5-3所示。

5-3  TransactionOption枚举类型支持5个值

   

Disabled

忽略当前上下文中的任何事务

NotSupported

使用非受控事务在上下文中创建组件

Required

如果事务存在则共享事务,并且如有必要则创建新事务

RequiresNew

使用新事务创建组件,而与当前上下文的状态无关

Supported

如果事务存在,则共享该事务

一般来说COM+中的组件需要Required Supported。当组件用于记录或查账时RequiresNew 很有用,因为组件应该与活动中其他事务处理的提交或回滚隔离开来。

派生类可以重载基类的任意属性。如OrderData选用Required,派生类仍然可以重载并指定RequiresNew或其他值。

COM+事务有手动处理和自动处理两种方式,自动处理就是在所需要自动处理的方法前加上[AutoComplete],根据方法的正常或抛出异常决定提交或回滚。手动处理就是调用ContextUtil类中的EnableCommitSetCompleteSetAbort方法。

实现步骤如下。

1.给程序添加强名

1)创建一对密钥

用来创建密钥的工具是称为sn.exe的共享工具。通常通过命令提示运行它,该工具可执行各种任务以生成并提取密钥。我们需要用以下方式来运行sn.exe

sn –k c:\key.snk

其中key.snk 代表将保存密钥的文件的名称。它的名称可以是任意的,不过习惯上带有.snk后缀名。

2)签名

这个文件必须在AssemblyKeyFile属性中引用,签名通常是在编译时进行的。签名时,用户可利用C#属性通知编译器应该使用正确的密钥文件对DLL进行签名。要做到这一点用户需要打开工程中的AssemblyInfo.cs文件并进行修改。

[assembly:AssemblyKeyFile(“..\\..\\key.snk)]

 

 

key.snk文件和项目文件在同一个文件夹内。

2.手动事务处理

创建一个项目用以实现事务处理的业务类ClassTran

代码示例:

                                     (示例位置:光盘\code\ch05\04\ClassTran\OrderData1

using System;

using System.Data.SqlClient;

using System.EnterpriseServices; //企业级服务COM+事务

namespace ClassTran

{

    [Transaction(TransactionOption.Required)]

    public class OrderData1 : ServicedComponent

    {

        //手动事务

        public string WorkTran()

        {

            try

            {

                ContextUtil.EnableCommit();

                Work1();

                Work2();

                ContextUtil.SetComplete();

                return "成功!";

            }

            catch (Exception ex)

            {

                ContextUtil.SetAbort();

                return "失败!";

            }

        }

        private void Work1()

        {

            string conString = "data source=127.0.0.1;database=codematic;

               user id=sa;password=";

            SqlConnection myConnection = new SqlConnection(conString);

            string strSql = "Insert Into P_Category(CategoryId,Name)

               values('1','test1')";

            SqlCommand myCommand = new SqlCommand(strSql, myConnection);

            myConnection.Open();

            int rows = myCommand.ExecuteNonQuery();

            myConnection.Close();

        }

        private void Work2()

        {

            string conString = "data source=127.0.0.1;database=codematic;

               user id=sa;password=";

            SqlConnection myConnection = new SqlConnection(conString);

            string strSql = "Insert Into P_Category(CategoryId,Name)

               values('2','test2')";

            SqlCommand myCommand = new SqlCommand(strSql, myConnection);

            myConnection.Open();

            int rows = myCommand.ExecuteNonQuery();

            myConnection.Close();

        }

    }

}

3.自动事务处理

代码示例:

在方法之前增加属性[AutoComplete(true)],这样如果方法执行时没有异常就默认提交,如果有异常则这个方法就会回滚。

                                    (示例位置:光盘\code\ch05\04\ClassTran\OrderData2

using System;

using System.Data.SqlClient;

using System.EnterpriseServices;//企业级服务COM+事务

namespace ClassTran

{

    [Transaction(TransactionOption.Required)]

    public class OrderData2 : ServicedComponent

    {

        //自动事务

        [AutoComplete(true)]

        public string WorkTran()

        {

            string msg = "";

            string conString = "data source=127.0.0.1;database=codematic;

               user id=sa;password=";

            SqlConnection myConnection = new SqlConnection(conString);

            myConnection.Open();                                   

            SqlCommand myCommand = new SqlCommand();

            myCommand.Connection = myConnection;           

            try

            {

                myCommand.CommandText = "update P_Product set Name='电脑2'

                     where Id=52";

                myCommand.ExecuteNonQuery();

                myCommand.CommandText = "update P_Product set Name='电脑3'

                where Id=53";

                myCommand.ExecuteNonQuery();

                msg ="成功!";

            }

            catch (Exception ex)

            {

                msg = "失败:"+ex.Message;              

            }

            finally

            {

                myConnection.Close();

            }

            return msg;

        }

    }

}

4.事务方法调用

代码示例:

                                      (示例位置:光盘\code\ch05\04\Web\WebForm4.aspx

protected void Button1_Click(object sender, EventArgs e)

{

    ClassTran.OrderData1 od1 = new ClassTran.OrderData1();

    od1.WorkTran();

}

protected void Button2_Click(object sender, EventArgs e)

{

    ClassTran.OrderData2 od2 = new ClassTran.OrderData2();

    od2.WorkTran();

}

在需要事务跨 MSMQ 和其他可识别事务的资源(例如SQL Server 数据库)运行的系统中,只能使用 DTC COM+ 事务,除此之外没有其他选择。DTC 协调参与分布式事务的所有资源管理器,也管理与事务相关的操作。

企业级服务COM+事务的前提及优缺点如下。

前提:

l  需要强名字。

l  使用事务的对象需要继承ServicedComponent

优势:

l  执行分布式事务,多个对象可以轻松地运行在同一个事务处理中,事务处理还可以自动登记。

l  获得COM+服务,诸如对象构建和对象池等。

缺点:

l  由于存在 DTC COM 互操作性开销,导致性能降低。

l  COM+ 1.0要求每个事务的隔离级别都设置为Serializable

l  使用Enterprise Services的事务总是线程安全的, 也就是说你无法让多个线程参与到同一个事务中。

 

选自《亮剑.NET. .NET深入体验与实战精要》一书 5.4 节。