`

坚持学习WF(16):WF中的事务

阅读更多

[置顶]坚持学习WF文章索引

在关系型数据库中支持事务已经有10几年了,在关系型数据库中如果你要有一组的更新要执行,你会先执行第一组更新,在执行第二组更新。。。。。。,如果最后所有的更新都成功,我们就将事务提交完成,如果中间发生了错误,我们会让事务回滚,到最初始的位置。我们使用事务来解决数据的完整性、一致性等等问题。

在.NET中我们可以使用SqlConnection和SqlTransaction来对关系型数据库管理事务,SqlTransaction包含Commit和Rollback方法来执行和回滚事务。我们也可以添加TransactionAttribute或AutoCompleteAttribute属性给一个企业服务组件(继承自ServicedComponent类并注册为企业服务),使用这个方式,会自动的以事务的方式来执行。在.NET2.0中又有了新的事务框架System.Transactions 。使用该命名空间包含的类可以编写自己的事务应用程序和资源管理器。具体地说,可以创建和参与(与一个或多个参与者)本地或分布式事务,如其中的Transaction和TransactionScope类。

在WF中提供了TransactionScopeActivity活动用来支持事务,当该活动执行时一个System.Transactions.Transaction实例就被创建了,如果TransactionScopeActivity中的子活动有一个有异常,就会执行回滚操作。

TransactionScopeActivity活动有一个TransactionOptions属性(WorkflowTransactionOptions的实例),包含两个属性来控制事务,其中一个是IsolationLevel,用来指定事务的隔离级别,具体如下:

           成员名称                                                  说明
Serializable 可以在事务期间读取可变数据,但是不可以修改,也不可以添加任何新数据
RepeatableRead 可以在事务期间读取可变数据,但是不可以修改。可以在事务期间添加新数据。
ReadCommitted 不可以在事务期间读取可变数据,但是可以修改它。
ReadUncommitted 可以在事务期间读取和修改可变数据。
Snapshot 可以读取可变数据。在事务修改数据之前,它验证在它最初读取数据之后另一个事务是否更改过这些数据。如果数据已被更新,则会引发错误。这样使事务可获取先前提交的数据值。
Chaos 无法覆盖隔离级别更高的事务中的挂起的更改。
Unspecified 正在使用与指定隔离级别不同的隔离级别,但是无法确定该级别。如果设置了此值,则会引发异常。

另一个是TimeoutDuration用于获取或设置表示事务超时期限的 TimeSpan。

当执行的工作流包含TransactionScopeActivity活动时,持久化服务也是必不可少的。因为当TransactionScopeActivity结束的时候会自动做持久化存储。除了持久化外还需要WorkflowCommitWorkBatchService,如果你不手动添加系统会默认给你添加一个默认的即DefaultWorkflowCommitBatchService。

我们在谈论的事务的时候举的最多的一个例子就是转账问题,下面我们也来实现一个转账的例子来说明WF中事务的使用,我们从A帐户转账到B帐户,我们首先要从A帐户中减去转账的数额,然后在将该数额加到B帐户上。如果前一个步骤完成了,而后一个步骤失败了,这个时候我们就需要事务来进行回滚操作。在做这个例子之前我们先做些准备工作,首先要建立持久化数据库,然后我们在建立一个数据库用来存储帐户余额的情况,一张表account有三个字段accountId,description,balance。下面先自定义一个转账活动AccountAdjustmentActivity.cs,代码如下:

public
partial class AccountAdjustmentActivity : Activity
    {      
        public static DependencyProperty AmountProperty
            = System.Workflow.ComponentModel.DependencyProperty.Register(
                "Amount", typeof(Decimal), typeof(AccountAdjustmentActivity));
        [Description("The amount of the balance adjustment")]
        [Category("ProWorkflow")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Decimal Amount
        {
            get
            {
                return ((Decimal)(base.GetValue(
                    AccountAdjustmentActivity.AmountProperty)));
            }
            set
            {
                base.SetValue(AccountAdjustmentActivity.AmountProperty, value);
            }
        }
      
        public static DependencyProperty AccountIdProperty
            = System.Workflow.ComponentModel.DependencyProperty.Register(
                "AccountId", typeof(Int32), typeof(AccountAdjustmentActivity));
        [Description("Identifies the account")]
        [Category("ProWorkflow")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Int32 AccountId
        {
            get
            {
                return ((Int32)(base.GetValue(
                    AccountAdjustmentActivity.AccountIdProperty)));
            }
            set
            {
                base.SetValue(AccountAdjustmentActivity.AccountIdProperty, value);
            }
        }
      
        public static DependencyProperty IsCreditProperty
            = System.Workflow.ComponentModel.DependencyProperty.Register(
                "IsCredit", typeof(Boolean), typeof(AccountAdjustmentActivity));
        [Description("True if this is a credit, false for a debit")]
        [Category("ProWorkflow")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Boolean IsCredit
        {
            get
            {
                return ((Boolean)(base.GetValue(
                    AccountAdjustmentActivity.IsCreditProperty)));
            }
            set
            {
                base.SetValue(AccountAdjustmentActivity.IsCreditProperty, value);
            }
        }
       
        protected override ActivityExecutionStatus Execute(
            ActivityExecutionContext executionContext)
        {
            using (SqlConnection connection = new SqlConnection(              
                    ConfigurationManager.ConnectionStrings
                    ["ProWorkflow"].ConnectionString))
            {
                connection.Open();

                if (!IsCredit)
                {                   
                    Decimal currentBal = GetCurrentBalance(
                        connection, AccountId);
                    if (currentBal < Amount)
                    {
                        throw new ArgumentException(
                            "余额不足,无法处理");
                    }
                }

              
                UpdateBalance(connection, AccountId, Amount, IsCredit);

                connection.Close();
            }

            return base.Execute(executionContext);
        }
     
        private Decimal GetCurrentBalance(
            SqlConnection connection, Int32 accountId)
        {
            Decimal balance = 0;
            String sql =
                @"select balance from account where accountId = @AccountId";

          
            SqlCommand command = new SqlCommand(sql);           
            SqlParameter p = new SqlParameter("@AccountId", accountId);
            command.Parameters.Add(p);
            command.Connection = connection;

            Object result = command.ExecuteScalar();
            if (result != null)
            {
                balance = (Decimal)result;
            }

            return balance;
        }

       
        private void UpdateBalance(SqlConnection connection,
            Int32 accountId, Decimal adjAmount, Boolean isCredit)
        {
            String sql;
            if (isCredit)
            {
                sql =
                    @"update account set balance = balance + @AdjAmount
                  where accountId = @AccountId
";
            }
            else
            {
                sql =
                    @"update account set balance = balance - @AdjAmount
                  where accountId = @AccountId
";
            }
           
            SqlCommand command = new SqlCommand(sql);          
            SqlParameter p = new SqlParameter("@AccountId", accountId);
            command.Parameters.Add(p);
            p = new SqlParameter("@AdjAmount", adjAmount);
            command.Parameters.Add(p);
            command.Connection = connection;
            command.ExecuteNonQuery();
        }
    }

增加App.config配置文件,代码如下:

<?
xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <!--connection string for workflow persistence database-->
    <add name="WorkflowPersistence" connectionString=
      "Integrated Security=SSPI;Initial Catalog=WorkflowPersistence;
      Data Source=localhost\SQLEXPRESS;Integrated Security=SSPI"
/>
    <!--connection string for the testing database-->
    <add name="ProWorkflow" connectionString=
      "Integrated Security=SSPI;Initial Catalog=ProWorkflow;
      Data Source=localhost\SQLEXPRESS;Integrated Security=SSPI"
/>
  </connectionStrings>
</configuration>

实现工作流(AccountTransferWorkflow.cs)如下图:

WFTransaction1 

我们要将creditActivity和debitActivity的AccountId和Amount属性绑定到工作流的三个依赖属性中,另外creditActivity的IsCredit属性设置为True,debitActivity的IsCredit属性设置为False,工作流代码如下:

public
sealed partial class AccountTransferWorkflow: SequentialWorkflowActivity
{
        public static DependencyProperty AmountProperty
           = System.Workflow.ComponentModel.DependencyProperty.Register(
               "Amount", typeof(Decimal), typeof(AccountTransferWorkflow));
        [Description("The amount of the balance adjustment")]
        [Category("ProWorkflow")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Decimal Amount
        {
            get
            {
                return ((Decimal)(base.GetValue(
                    AccountTransferWorkflow.AmountProperty)));
            }
            set
            {
                base.SetValue(AccountTransferWorkflow.AmountProperty, value);
            }
        }
     
        public static DependencyProperty FromAccountIdProperty
            = System.Workflow.ComponentModel.DependencyProperty.Register(
                "FromAccountId", typeof(Int32), typeof(AccountTransferWorkflow));
        [Description("Identifies the account")]
        [Category("ProWorkflow")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Int32 FromAccountId
        {
            get
            {
                return ((Int32)(base.GetValue(
                    AccountTransferWorkflow.FromAccountIdProperty)));
            }
            set
            {
                base.SetValue(AccountTransferWorkflow.FromAccountIdProperty, value);
            }
        }
      
        public static DependencyProperty ToAccountIdProperty
            = System.Workflow.ComponentModel.DependencyProperty.Register(
                "ToAccountId", typeof(Int32), typeof(AccountTransferWorkflow));
        [Description("Identifies the account")]
        [Category("ProWorkflow")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Int32 ToAccountId
        {
            get
            {
                return ((Int32)(base.GetValue(
                    AccountTransferWorkflow.ToAccountIdProperty)));
            }
            set
            {
                base.SetValue(AccountTransferWorkflow.ToAccountIdProperty, value);
            }
        }
public AccountTransferWorkflow()
{
InitializeComponent();
}
}

实现宿主程序
我们在宿主中进行了两次转账操作,两个帐户的初始化余额都是100,第一次是成功的,第二次由于转账的数额大于帐户的余额,发生了异常,这个时候转账不成功,我们进行回滚操作,宿主程序的主要代码如下:

public class
AccountTransferTest
   
{
        public static void Run()
        {
            using (WorkflowRuntimeManager manager
                = new WorkflowRuntimeManager(new WorkflowRuntime()))
            {
                AddServices(manager.WorkflowRuntime);
                manager.WorkflowRuntime.StartRuntime();

                Console.WriteLine("第一次转账开始");
                DisplayTestData(1001, 2002, "转账前");               
               
Dictionary<String, Object> wfArguments = new Dictionary<string, object>();
                wfArguments.Add("FromAccountId", 1001);
                wfArguments.Add("ToAccountId", 2002);
                wfArguments.Add("Amount", (Decimal)25.00);
                WorkflowInstanceWrapper instance = manager.StartWorkflow(
                    typeof(CaryTransactionAP.AccountTransferWorkflow), wfArguments);
                manager.WaitAll(5000);
                if (instance.Exception != null)
                {
                    Console.WriteLine("EXCEPTION: {0}",instance.Exception.Message);
                }
                DisplayTestData(1001, 2002, "转帐后");
                Console.WriteLine("第一次转账结束\n\r");

                Console.WriteLine("第二次转账开始");
                DisplayTestData(1001, 2002, "转账前");
                wfArguments = new Dictionary<string, object>();
                wfArguments.Add("FromAccountId", 1001);
                wfArguments.Add("ToAccountId", 2002);
                wfArguments.Add("Amount", (Decimal)200.00);
                instance = manager.StartWorkflow(
                    typeof(CaryTransactionAP.AccountTransferWorkflow), wfArguments);
                manager.WaitAll(5000);
                if (instance.Exception != null)
                {
                    Console.WriteLine("发生异常: {0}", instance.Exception.Message);
                }
                DisplayTestData(1001, 2002, "转账后");
                Console.WriteLine("第二次转账结束\n\r");
            }
        }
       

        private static void AddServices(WorkflowRuntime instance)
        {
            SqlWorkflowPersistenceService persistence = new SqlWorkflowPersistenceService(
                ConfigurationManager.ConnectionStrings["WorkflowPersistence"].ConnectionString,
                    true, new TimeSpan(0, 2, 0), new TimeSpan(0, 0, 5));
            instance.AddService(persistence);
        }

        private static void DisplayTestData(Int32 acctId1, Int32 acctId2, String desc)
        {
            using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings
                    ["ProWorkflow"].ConnectionString))
            {
                connection.Open();
                Decimal balance = GetCurrentBalance(connection, acctId1);
                Console.WriteLine(" {0} AccountId为 {1}的金额为: {2}",desc, acctId1, balance);
                balance = GetCurrentBalance(connection, acctId2);
                Console.WriteLine(" {0} AccountId为 {1}的金额为: {2}", desc, acctId2, balance);
                connection.Close();
            }
        }

       private static Decimal GetCurrentBalance(SqlConnection connection, Int32 accountId)
        {
            Decimal balance = 0;
            String sql =@"select balance from account where accountId = @AccountId";
            SqlCommand command = new SqlCommand(sql);
            SqlParameter p = new SqlParameter("@AccountId", accountId);
            command.Parameters.Add(p);
            command.Connection = connection;
            Object result = command.ExecuteScalar();
            if (result != null)
            {
                balance = (Decimal)result;
            }
            return balance;
        }
}





运行后的结果如下图:


WFTransaction2

分享到:
评论

相关推荐

    坚持学习WF,WF学习教程

    坚持学习WF(16):WF中的事务 在关系型数据库中支持事务已经有10几年了,事务用来解决数据的完整性、一致性等等问题。在WF中提供了TransactionScopeActivity活动用来支持事务,当该活动执行时一个System.Transactions...

    WF教程_坚持学习WF

    坚持学习WF(2):WF创作模式和设计时工具 坚持学习WF(3):WF框架概览 2.3两篇主要全面的阐述了WF框架和Visual Studio对开发WF的一些设计时的工具。 坚持学习WF(4):活动(Activity)和依赖属性(DependencyProperty) ...

    坚持学习WF

    坚持学习WF(16):WF中的事务 在关系型数据库中支持事务已经有10几年了,事务用来解决数据的完整性、一致性等等问题。在WF中提供了TransactionScopeActivity活动用来支持事务,当该活动执行时一个System.Transactions...

    坚持学习WF(1):从HelloWorld开始 源码

    在本篇“坚持学习WF(1):从HelloWorld开始”的源码分析中,我们将深入理解WF的基础知识和创建第一个工作流的步骤。 首先,让我们了解WF的基本概念。WF提供了一种模型化业务流程的方式,通过活动(Activities)来表示...

    坚持学习WF(5):自定义活动(CustomActivity) 源码

    总的来说,自定义活动是WF中实现业务逻辑灵活性的关键。通过深入理解和实践自定义活动的创建,开发者可以构建出符合业务需求的高效、可维护的工作流应用程序。通过对`CaryActivity`源码的学习和分析,我们可以更好地...

    Apress - Beginning WF: Windows Workflow in .NET 4.0 Mar 2010

    本书《Beginning WF: Windows Workflow in .NET 4.0》是一本详细介绍如何在.NET 4.0环境中使用Windows Workflow Framework(WF)进行应用开发的专业指南。本书由Mark J. Collins撰写,于2010年3月19日由Apress出版社...

    WF工作流学习资料

    3. **WF活动**:WF的核心是活动,它们是工作流中的最小执行单元。活动可以是内置的,如If条件判断、Assign赋值,也可以是自定义的,满足特定业务需求。活动可以通过组合和嵌套形成复杂的工作流逻辑。 4. **WF生命...

    【翻译】WF从入门到精通(第一章):WF简介源代码下载

    活动是WF中最基本的构建块,它可以代表一个业务操作或者是一个更复杂的工作流子流程。工作流是由一系列活动组成的逻辑流程,它们共同定义了一个业务过程。运行时是WF执行工作流的地方,它负责管理工作流的生命周期,...

    (2007 Apress)Pro WF Windows Workflow in .NET3.0.pdf

    - **活动(Activity)**:WF中的基本执行单元,代表了工作流的一部分逻辑。 - **工作流引擎**:负责解释活动并执行工作流的组件。 - **持久化**:WF支持将工作流的状态保存到持久存储中,以便在后续时刻恢复执行。 ##...

    WF学习从入门到提高

    活动是WF中最基本的构建块,它们代表了流程中的一个操作或步骤。你可以使用内置的活动,如If、Parallel、SequentialWorkflowActivity等,也可以自定义活动以满足特定需求。在设计工作流时,你会用到这些活动,通过...

    Beginning WF Windows Workflow in .NET 4.0

    书中还探讨了在使用WF时应遵循的设计模式和最佳实践,例如如何合理地使用事务、如何处理错误和异常、如何优化性能等。这些指导原则对于构建稳定、高效的工作流系统至关重要。 #### 5. 集成与扩展 WF提供了丰富的...

    wf工作流资料<转自网上的笔记>

    接着,"坚持学习WF(2):WF创作模式和设计时工具"会深入到WF的设计环境,比如Visual Studio中的工作流设计器,讲解如何利用这些工具来创建和编辑工作流。此外,还会介绍几种常见的工作流模式,如顺序工作流、并行工作...

    爱普生WF-7720_WF-7710_WF-7210维修手册 含报错代码解释

    《爱普生WF-7720、WF-7710、WF-7210维修手册:含报错代码解释》 本手册详细介绍了爱普生WF-7720系列、WF-7710系列以及WF-7210系列彩色喷墨打印机的拆机分解、故障代码解析及解决思路。这些打印机支持云打印功能,是...

    wf4cookbook中文

    WF4 Cookbook中文版是针对Microsoft Windows Workflow Foundation 4.0(WF4)的一份技术指南,旨在帮助开发者...文档“wf4Cookbook翻译.docx”包含了详细的技术说明和示例代码,是学习和参考WF4工作流开发的重要资源。

    WF从入门到精通(源码)

    9. **异常处理与事务**:理解如何在WF中处理错误和异常,以及如何利用WF的事务支持确保流程的一致性。 10. **监控与调试**:学习如何使用Visual Studio和WF提供的工具进行工作流的监控和调试,这对于识别和解决问题...

    图解爱普生 WF-7620 WF-7610 WF-7110 喷墨打印一体机中文维修手册.zip

    爱普生WF-7620、WF-7610和WF-7110是爱普生公司推出的高效办公喷墨打印一体机,它们都具备打印、扫描、复印和传真功能,适合中小型企业或家庭办公室使用。这些机型采用爱普生的DuraBrite Ultra颜料墨水,能提供高质量...

Global site tag (gtag.js) - Google Analytics