Windows Workflow Foundation Windows Workflow Foundation
В этом разделе описывается модель программирования, примеры и средства Windows Workflow Foundation (WF). This section describes the programming model, samples, and tools of the Windows Workflow Foundation (WF).
в этом разделе In This Section
Руководство по документации по рабочему процессу Windows Набор предлагаемых тем для чтения, в зависимости от ваших знаний (новичков в работе) и требований. Guide to the Windows Workflow Documentation A set of suggested topics to read, depending upon your familiarity (novice to well-acquainted), and requirements.
Новые возможности Windows Workflow Foundation Обсуждаются изменения в нескольких парадигмах разработки из предыдущих версий. What’s New in Windows Workflow Foundation Discusses the changes in several development paradigms from previous versions.
Новые возможности Windows Workflow Foundation в .NET Framework 4,5 Описание новых возможностей в Windows Workflow Foundation в .NET Framework 4.6.1 .NET Framework 4.6.1 . What’s New in Windows Workflow Foundation in .NET Framework 4.5 Describes the new features in Windows Workflow Foundation in .NET Framework 4.6.1 .NET Framework 4.6.1 .
Особенности Windows Workflow Foundation компонентов Описание новых функций в Windows Workflow Foundation в .NET Framework 4. Windows Workflow Foundation Feature Specifics Describes the new features in Windows Workflow Foundation in .NET Framework 4.
Общие сведения о рабочем процессе Windows Набор разделов, в которых обсуждаются более крупные понятия Windows Workflow Foundation. Windows Workflow Conceptual Overview A set of topics that discusses the larger concepts behind Windows Workflow Foundation.
Учебник по начало работы Набор пошаговых руководств, посвященных программированию Windows Workflow Foundation приложений. Getting Started Tutorial A set of walkthrough topics that introduce you to programming Windows Workflow Foundation applications.
Windows Workflow Foundation программирование Набор разделов учебника, которые следует понимать, чтобы стать хорошо знакомым WF WF программистам. Windows Workflow Foundation Programming A set of primer topics that you should understand to become a proficient WF WF programmer.
Расширение Windows Workflow Foundation Набор разделов, в которых обсуждается расширение или Настройка Windows Workflow Foundation в соответствии со своими потребностями. Extending Windows Workflow Foundation A set of topics that discusses how to extend or customize Windows Workflow Foundation to suit your needs.
Примеры рабочих процессов Windows Содержит примеры приложений, демонстрирующие функции и сценарии WF. Windows Workflow Samples Contains sample applications that demonstrate WF features and scenarios.
Программирование в Windows Workflow Foundation Windows Workflow Foundation Programming
Этот раздел содержит набор разделов руководства, которые следует понимать, чтобы стать хорошо знакомым программистом Windows Workflow Foundation (WF). This section contains a set of primer topics that you should understand to become a proficient Windows Workflow Foundation (WF) programmer.
в этом разделе In This Section
Разработка рабочих процессов Designing Workflows
Разделы, которые описывают парадигмы управления потоками, используемые в разработке рабочих процессов. Topics that describe the flow-control paradigms used in workflow development.
Использование и создание действий Using and Creating Activities
Разделы, описывающие предоставляемые системой действия, доступные в Windows Workflow Foundation (WF). Topics that describes the system-provided activities available in Windows Workflow Foundation (WF).
Модель данных Windows Workflow Foundation Windows Workflow Foundation Data Model
Разделы, которые описывают переменные, аргументы и выражения в WF WF . Topics that describe variables, arguments and expressions in WF WF .
Ожидание входных данных в рабочем процессе Waiting for Input in a Workflow
Разделы, в которых описаны способы использования закладок и действия по обмену сообщениями. Topics that describes how to use bookmarks and messaging activities.
Исключения, транзакции и компенсация Exceptions, Transactions, and Compensation
Разделы, в которых описаны способы использования обработчиков исключений, транзакций и компенсации для обработки ошибок во время выполнения. Topics that describe how to use exception handlers, transactions, and compensation to handle run-time errors.
Размещение рабочих процессов Hosting Workflows
Разделы, подробно описывающие программирование для узла рабочих процессов. Topics that describe the details for writing workflow host applications.
Динамическое обновление Dynamic Update
Описывает способ обновления определения рабочего процесса сохраненного экземпляра рабочего процесса с помощью динамического обновления. Describes how to use dynamic update to update the workflow definition of a persisted workflow instance.
Службы рабочего процесса Workflow Services
Подразделы, описывающие модель программирования, которая поддерживает написание служб декларативно. Topics that describe the programming model that supports writing services declaratively.
Сохраняемость рабочего процесса Workflow Persistence
Разделы, в которых описаны параметры автоматического или ручного сохранения данных рабочих процессов и выгрузки рабочих процессов из памяти. Topics that describe the options for automatically or manually persisting workflow data and unloading workflows from memory.
Руководство по миграции Migration Guidance
Разделы, описывающие перенос рабочих процессов из предыдущих версий Windows Workflow Foundation (WF). Topics that describe how to migrate workflows from previous versions of Windows Workflow Foundation (WF).
Отслеживание и трассировка рабочих процессов Workflow Tracking and Tracing
Разделы, описывающие отслеживание, трассировку рабочих процессов и способ использования этих возможностей для мониторинга рабочих процессов. Topics that describe workflow tracking and tracing and how these features are used for monitoring workflow applications.
Безопасность рабочих процессов Workflow Security
Описывает, как обеспечить безопасность рабочего процесса при использовании SQL и Windows Communication Foundation (WCF). Discusses how to keep your workflow secure when using SQL and Windows Communication Foundation (WCF).
Производительность Windows Workflow Foundation 4 Windows Workflow Foundation 4 Performance
Описывает Windows Workflow Foundation 4 и сравнивает их с предыдущей версией WF. Discusses Windows Workflow Foundation 4 performance and compares it to the previous version of WF.
Programming Windows Workflow Foundation: Practical WF Techniques and Examples using XAML and C#
1 Programming Windows Workflow Foundation: Practical WF Techniques and Examples using XAML and C# A C# developer’s guide to the features and programming interfaces of Windows Workflow Foundation K. Scott Allen Chapter 3 «Sequential Workflows»
2 In this package, you will find: A Biography of the authors of the book A preview chapter from the book, Chapter 3 «Sequential Workflows A synopsis of the book s content Information on where to buy this book About the Author Scott Allen Scott Allen is a software architect with 14 years of experience in commercial software development. Scott has worked on platforms ranging from 8-bit embedded systems to highly scalable and distributed web applications. For the last six years, Scott has concentrated on the.net platform, and is a Microsoft MVP. Scott presents at local code camps and national conferences. Scott is a founder of the website OdeToCode.com, where he publishes articles and maintains a blog. Scott previously coauthored Building Websites with the ASP.NET Community Starter Kit for Packt Publishing, and has also published articles in MSDN Magazine and Dr. Dobb’s Journal. I’d like to thank Packt’s Douglas Paterson for putting up with my antics over the years. I’d also like to acknowledge the rest of the Packt team, including Patricia Weir, Abhijeet Deobhakta, and Viraj Joshi, for their efforts. Dan Kahler and Nikhil Bangera made this a better book with their diligent reviewing efforts, and I am grateful. Finally, there is Beaker. While I was writing, and the rest of the world was sleeping, Beaker would be on my desk or curled next to my feet. She’d be sleeping too, but she was always close by until she passed away. I miss you, Beaker.
3 About the Reviewers Dan Kahler Dan Kahler is a Senior Engineer with Verizon Business. With over eight years of experience developing and administering Windows and Web-based solutions, he specializes in using Microsoft technologies to simplify and automate day-to-day system administration tasks and to integrate line-of-business applications. Dan previously contributed to the Microsoft Log Parser Toolkit (Syngress, ISBN: ) as a contributing author, and contributed to the Microsoft Internet Information Services (IIS) 6.0 Resource Kit (Microsoft Press,ISBN: ) as a technical reviewer and tester. He is active in the Baltimore.NET user group ( Dan currently resides in Eldersburg, Maryland with his wife Shannon and children Nicole and Ethan.
4 Programming Windows Workflow Foundation Windows Workflow Foundation (WF) is a technology for defining, executing, and managing workflows. It is part of the.net Framework 3.0 and will be available natively in the Windows Vista operating system. Windows Workflow Foundation might be the most significant piece of middleware to arrive on the Windows platform since COM+ and the Distributed Transaction Coordinator. The difference is, not every application needs a distributed transaction, but nearly every application does have a workflow encoded inside. This book will help you add that workflow power to your applications. What This Book Covers Chapter 1 introduces us to the concept of workflow and describes how Windows Workflow can solve the difficult problems inherent in workflow solutions. We’ll become familiar with activities as the basic building blocks of a workflow definition and demonstrate how to author a simple workflow using Visual Studio This chapter also describes the runtime services available with WF. By the end of the chapter we will be able to identify the primary features of Windows Workflow. Chapter 2 concentrates on authoring workflows. Specifically, we’ll look at how to build workflows with C#, and with extensible application markup language (XAML). Looking at the workflow compiler, we’ll have a better understanding of how WF uses code generation to produce classes from workflow markup, and how this generated code can combine with our handwritten code to produce a workflow type. This chapter will provide the fundamental knowledge needed to understand how WF operates during the compilation phase. In Chapter 3, we will turn our attention to sequential workflows. We will examine the Sequence Activity and learn about the events fired by the workflow runtime during the life of a workflow instance. Using Visual Studio, we will build workflows that accept parameters and communicate with a host process by invoking methods and listening for events. The chapter concludes with a workflow example that raises an exception and uses a fault handler. Chapter 4 examines each activity in the WF base activity library. We will look at the control flow activities, communication activities, and transaction-oriented activities. The chapter also examines web service activities, rule-centric activities, and state activities. The goal of this chapter is to make us aware of all the capabilities provided by the base activity library, with an eye towards understanding how each activity can solve a particular problem.
5 With an understanding of what is available in the base activity library, we can look at building our own custom activities in Chapter 5. This chapter examines the motivations for building custom activities, and provides examples of building a custom activity using both a compositional approach and a derivation approach. We’ll see how to build a custom validator and designer for our activity, and also understand the advantages of using dependency properties. The chapter ends by covering the execution context, which we must understand to build robust activities. Chapter 6 covers the workflow runtime, workflow diagnostics, and the out of the box services provided for WF by Microsoft. The chapter demonstrates how to configure services both declaratively and programmatically. We’ll see examples of how to use a scheduling service, persistence service, and tracking service. The chapter provides enough information to allow a developer to select and configure the services needed for a wide variety of scenarios and environments. Chapter 7 focuses on building event driven workflows using state machines. We’ll see how WF models the traditional state machine using activities, and we will build a workflow to handle external events and react with state transitions. We’ll also see how to track and examine the history of state machine execution. The chapter ends with an examination of a hierarchical state machine, which provides all the knowledge we need to tackle tough problems with event driven workflows. Chapter 8 is dedicated to workflow communications. The chapter explains how to use correlated local services for communication with a host process, and web service activities for communication across a network. By the end of the chapter we’ll uncover the queuing service that is used behind the scenes of a workflow to coordinate and deliver messages. Finally, Chapter 9 is about rules and conditions in Windows Workflow. This discusses the role of business rules in software development and provides examples of how WF’s rules engine can take away some of the burden of rule development. The chapter takes an in-depth look at rule execution in the Policy Activity, and recording diagnostic information about rule evaluation. We’ll come away with the knowledge we need to build rule based solutions using Windows Workflow.
6 Sequential Workfl ows Windows Workflow offers two workflow execution styles out of the box: sequential and event-driven. A sequential workflow completes one activity and moves to the next, executing a sequence of consecutive steps. As an example, a sequential workflow could model the process of moving records from a website’s compressed log file into a database table. Step 1 would download the compressed log file. Step 2 would decompress the log file. Step 3 would bulk insert records from the log file into a table, and step 4 would create summary statistics from the new records. Even though a sequential workflow can use branches, loops, and receive external events, it is mostly predictable and marches inevitably forward to completion. Event-driven workflows, on the other hand, rely on external events to drive them to a finishing point. Event-driven workflows model a workflow as a state machine. A state machine consists of a set of states (including an initial state and a final state), and a set of events. The state machine is always in one of the defined states, and cannot transition to a new state until an event arrives. With these differences in mind, let’s explore sequential workflows, and return to state machines in Chapter 7. The SequenceActivity Activities are the basic building blocks in Windows Workflow, and a sequential workflow itself is an activity a SequentialWorkflowActivity to be precise. The SequentialWorkflowActivity class derives from the SequenceActivity class, which in turn derives from the CompositeActivity class. These superclasses dictate the behavior and characteristics of a sequential workflow. The class diagram as shown on the next page depicts this class hierarchy.
7 Sequential Workflows The CompositeActivty class provides the logic for an activity to contain one or more child activities. A sequential workflow will typically contain multiple children (and the children may also be CompositeActivity objects with their own children). The SequenceActivity class provides the logic to execute child activities. The SequenceActivity iterates through its children in a forward-only direction, executing each child once and then moving to the next child. When the last child activity is complete, the sequence is finished. As we mentioned earlier, this doesn’t mean a sequential activity cannot loop or branch, but it does mean execution always moves forward. There is no mechanism available to jump back to an arbitrary activity in the workflow. Simple Flow A s an example, let’s start building a simple sequential workflow. Our workflow will increment a counter and write the counter value to the screen. To start writing our workflow we will use the Sequential Workflow Console Application C# template from the Visual Studio New Project menu. The project wizard will give us an application and a workflow definition named Workflow1. Right-click on the workflow and select View Code to open the code-beside file. Inside, we’ll place the highlighted code below: public partial class Workflow1 : SequentialWorkflowActivity int counter = 0; [ 52 ]
8 Chapter 3 B ack in the workflow design window, we’ll drag a CodeActivity from the Toolbox window to the design surface. Sequential workflows and sequence activities in general always start execution at the top of the flow and end at the bottom. Drop points (small green plus signs) will appear along the line of execution whenever we are dragging an activity nearby. In the Properties window, we can give this activity a name of IncrementCounter. We’ll drag a second CodeActivity from the Toolbox and drop the activity beneath our first activity. We’ll give this activity the name of WriteCounter. Our workflow will look as below: Next, we can give our IncrementCounter an ExecuteCode handler by double-clicking the activity in the workflow designer. The designer will generate a skeletal method for the handler, and all we have to do is supply the internal details. We’ll add one line of code to increment the counter field. private void IncrementCounter_ExecuteCode(object sender, EventArgs e) counter++; We can return to the designer view, double-click WriteCounter, and add the following code: private void WriteCounter_ExecuteCode(object sender, EventArgs e) Console.WriteLine(«The value of counter is 0.», counter); [ 53 ]
9 Sequential Workflows If we run this workflow, we’ll only see one line of output, telling us, The value of counter is 1. The sequential workflow executes the two child activities in the order they appear, and after both have executed, the sequential workflow is complete. Next, we’ll add some control flow activities to make the workflow more interesting. Sequences Inside Sequences The WhileActivity allows us to execute a single child activity until a condition returns false. We can drag a WhileActivity from the Toolbox into our sequential workflow as the first activity in the workflow. An empty WhileActivity will display Drop An Activity Here on the designer screen. It might sound surprising that we can place only a single child activity inside the WhileActivity. The WhileActivity derives from the CompositeActivity class, meaning it will hold child activities, but it isn’t derived from SequenceActivity, which provides the logic to execute multiple child activities. Instead, the WhileActivity executes only a single child activity. If we want both of our code activities to execute inside the WhileActivity, we need to enclose the activities inside a SequenceActivity. We can drag a SequenceActivity from the Toolbox and drop the activity inside the WhileActivity. Then we can move both of our code activities inside the sequence. Our workflow would now look as shown in the screenshot on the next page. [ 54 ]
10 Chapter 3 Before we can run the workflow, we’ll need to provide a condition for the WhileActivity to evaluate. We can write conditions into an external rules file, or directly in code. Let’s add the following method to the code behind the workflow: private void CheckCounter(object sender, ConditionalEventArgs e) e.result = false; if (counter 11 Sequential Workflows we can set Condition to Code Condition, and select CheckCounter in the drop-down list of ava ilable conditions. Now our workflow will print ten lines to the console, each with a higher value for the counter field. Of course, not all workflows can run in isolation using only a counter field and the console. We often need to pass data to a workflow and fetch data from a workflow. Workflows and the Outside World For many workflows, an important step will be to decide how the workflow will interact with an application. How do we know if a workflow finished successfully or threw an exception? How do we get data into a workflow instance? When the workflow completes, how do we get data out? Technically, there are an infinite number of solutions to these questions. In this section, however, we are going to cover some of the fundamental techniques. The basic mechanisms for communicating with a workflow include events, methods, and workflow parameters. An application can both raise events to a workflow instance and receive lifecycle events about a workflow instance from the workflow runtime. These lifecycle events are the first topic for discussion. Workflow Instance Lifetime Events The WorkflowRuntime class is the gateway to all running workflows. WorkflowRuntime exposes a number of events we can use to detect changes in a running workflow. These events are listed in the following table: Name WorkflowAborted WorkflowCompleted WorkflowCreated WorkflowIdled WorkflowLoaded WorkflowPersisted Description Occurs when an instance aborts. The WorkflowInstance class includes an Abort method to abort a workflow. Occurs when the instance completes, and includes a WorkflowCompletedEventArgs parameter to retrieve any output parameters. Occurs after we create a workflow with the WorkflowRuntime’s CreateWorkflow method. Occurs when a workflow enters an idle state. A workflow becomes idle when it is waiting for a timer or external event to take place, for instance. Occurs when a persistence service has restored a workflow instance into memory to continue execution. Occurs when a persistence service persists a workflow. A workflow may persist and then unloaded from memory when it is in the idle state and waiting for an event. [ 56 ]
12 Chapter 3 Name WorkflowSuspended WorkflowResumed WorkflowStarted WorkflowTerminated Description Occurs when the runtime suspends a workflow, typically due to a SuspendActivity in the workflow. Occurs when workflow execution continues after a suspension. Occurs when a workflow firsts starts execution. Occurs when a workflow terminates, typically due to an unhandled exception. WorkflowTerminatedEventArgs will include the exception object. WorkflowUnloaded Occurs when the runtime unloads a workflow from memory, typically due to the workflow being idle. To see these events in action we can look at the project named chapter3_sequential in the code accompanying the book. The WorkflowEvents.xoml file in the project includes a Code activity and a Suspend activity. The design mode of the workflow should look as shown in the screenshot below: T he Code activity in this workflow only writes a message to the console. The code behind WorkflowEvents is shown below: using System; using System.Workflow.Activities; namespace chapter3_sequential [ 57 ]
13 Sequential Workflows public partial class WorkflowEvents : SequentialWorkflowActivity private void codeactivity1_executecode(object sender, EventArgs e) Console.WriteLine(«Executing. «); In Program.cs, the application wires up all of the WorkflowRuntime events using code like the following: runtime.workflowcreated += new EventHandler (runtime_WorkflowCreated); runtime.workflowidled += new EventHandler (runtime_WorkflowIdled); E ach event handler writes a message to console, which enables us to see which events fire and when. static void runtime_workflowidled(object sender, WorkflowEventArgs e) Console.WriteLine(«Workflow idled»); static void runtime_workflowcreated(object sender, WorkflowEventArgs e) Console.WriteLine(«Workflow created»); Two of the events (Terminated and Completed) need to call the Set method of the program’s WaitHandle object. As discussed in Chapter 1, the default runtime behavior is to execute our workflow on a background thread. The program will need to block the main thread on the WaitHandle object using the WaitOne method. The WaitOne method keeps the main thread waiting until the Set method signals completion of the workflow. If we didn’t wait for the completion signal, the main thread would exit and the application would terminate before the workflow has a chance to execute. Here is an event handler for the Terminated activity, which calls Set and writes information about the unhandled exception to the console. static void runtime_workflowterminated(object sender, WorkflowTerminatedEventArgs e) [ 58 ]
14 Chapter 3 Console.WriteLine(«Workflow terminated»); Console.WriteLine(«\tException: » + e.exception.message); waithandle.set(); Executing our console mode program produces the output shown in the screenshot below: The output indicates that the workflow fired a created event, a started event, and then ex ecuted the code inside of the first CodeActivity. The next activity was the SuspendActivty highlighted in the screenshot below. The activity has an Error property available in the Toolbox window, and we’ve set this property to the string literal intentionally suspended. The suspendactivity tells the runtime to temporarily halt execution of the workflow instance. We might want to suspend a workflow if execution reaches a point where processing cannot continue without intervention, but we don’t want [ 59 ]
15 Sequential Workflows to terminate the workflow with an exception. In this scenario, we are suspending the workflow just to see the WorkflowSuspended event fire. The contents of the Error property will be available to the event handler in an instance of the WorkflowSuspendedEventArgs class. static void runtime_workflowsuspended(object sender, WorkflowSuspendedEventArgs e) Console.WriteLine(«Workflow suspended»); Console.WriteLine(«\tReason: » + e.error); e.workflowinstance.resume(); When the WorkflowSuspended event fires, we write out a message and immediately ask the workflow instance to resume processing. The workflow picks up where it left off, and runs to completion. Workflow instance events are not the only technique available for monitoring the execution of a workflow. A workflow tracking service can receive exceptionally granular information about the state of a workflow. WF provides a SqlTrackingService class to log tracking information to a SQL Server database, but we can also implement and plug a custom tracking service into the runtime. We cover tracking services in Chapter 6. Of course, there will be times when we need to get information into a workflow before execution begins. Parameters are one technique for feeding data to a workflow. Workflow Parameters An overloaded version of the runtime’s CreateWorkflow method allows us to pass parameters into a new workflow instance. Parameters are a collection of name and value pairs in an instance of the Dictionary generic class. Dictionary parameters = new Dictionary (); parameters.add(«firstname», «Scott»); parameters.add(«lastname», «Allen»); instance = runtime.createworkflow(typeof(workflowparameters), parameters); instance.start(); [ 60 ]
16 When the workflow runtime creates a new workflow instance, it tries to find a companion property for each named parameter value. A companion property is a public, writable property on the workflow object with the same name as the parameter. For example, the following workflow class will accept incoming parameters with the names FirstName and LastName. [ 61 ] Chapter 3 public partial class WorkflowParameters : SequentialWorkflowActivity public string FirstName set _firstname = value; private string _firstname; public string LastName set _lastname = value; private string _lastname; public string FullName get return _fullname; private string _fullname; private void codeactivity1_executecode(object sender, EventArgs e) _fullname = String.Format(«0 1», _firstname, _lastname); //. If we try to pass a FullName parameter, the runtime will throw an exception. The FullName property is a read-only property. If the runtime cannot find a public, writable companion property for an incoming parameter, it throws a System. ArgumentException exception. The reverse is not true, however. Parameters are optional, and we don’t need to specify an input parameter for every public writable property. The activities inside of a workflow don’t need to perform any special tricks to fetch the parameter values. The runtime will have placed all the parameters into properties before execution begins. In the above code, we have an ExecuteCode event handler for a CodeActivity concatenating the backing fields for the first and last name parameters into a full name field. As you might have guessed, we will be able to retrieve the FullName property as an output parameter.
17 Sequential Workflows When a workflow completes, the runtime raises a WorkflowCompleted event and passes along a WorkflowCompletedEventArgs object. WorkflowCompletedEventArgs contains an OutputParameters property, which is a Dictionary collection of all output parameters. The runtime will copy the value of each public, readable workflow property int o the OutputParameters collection. static void runtime_workflowcompleted(object sender, WorkflowCompletedEventArgs e) Console.WriteLine(«Workflow completed»); Console.WriteLine(«\tOutput parameters: «); foreach (KeyValuePair pair in e.outputparameters) Console.WriteLine(«\t\tName: 0 Value: 1», pair.key, pair.value); waithandle.set(); The output of the above code will look as shown below: Raising Events and Invoking Methods Many developers will want to establish a more formal mechanism for interacting with a workflow. Although the Dictionary appr oach is an easy and flexible approach to putt ing data into a workflow, it’s easy to forget a parameter, or forget the name of a parameter. Also, we can only put data into a workflow using parameters at the start of a workflow, and only get data out at the end. Many workflows will need to exchange data with the host at various times during execution. To achieve this goal we can use events and method calls. This section is going to discuss the fundamentals of communication for data transfer, and we will return with more depth in Chapter 8. [ 62 ]
18 [ 63 ] Chapter 3 Service Contracts A wo rkflow and its host can exchange data via a local communication service (LCS). An LCS allows events and method calls between a workflow and a host. Behind the scenes, the workflow runtime works with an LCS to intercept communications and provide additional services (like queuing an event until a workflow is in a state to accept the event). Communication via an LCS requires a messaging contract, and in.net, a contract is synonymous with an interface. Interfaces will define the events and methods that an LCS will expose. Events pass data from the host to a workflow, while methods pass data from a workflow to the host. As an example, the following interface defines a bug tracking service with one event and one method: [ExternalDataExchange] interface IBugFlowService void AssignBug(Bug bug); event EventHandler BugAdded; The workflow can invoke the AssignBug method to pass a Bug object to its host. Likewise, the host can raise the BugAdded event and pass data into a workflow via an event argument. The ExternalDataExchange attribute will identify this interface as a messaging contract. Windows Workflow will look for this piece of metadata when we add local services to the runtime. The Bug class we are passing is a simple data container with three properties, and looks like the following: [Serializable] public class Bug public Bug(int id, string title, string description) _id = id; _title = title; _description = description; public Bug() private int _id; public int ID get return _id;
19 Sequential Workflows set _id = value; private string _title; public string Title get return _title; set _title = value; private string _description; public string Description get return _description; set _description = value; All objects passing between a workflow and a host must be serializable objects. For the Bug class, we decorate the class with a Serializable attribute. In order to pass a Bug via an event, we will need a serializable event argument class, shown below: [Serializable] public class BugAddedArgs : ExternalDataEventArgs public BugAddedArgs(Guid instanceid, Bug newbug) : base(instanceid) _bug = newbug; private Bug _bug; public Bug NewBug get return _bug; set _bug = value; In addition to being serializable, LCS events must derive from the ExternalDataEventArgs class (shown in the screenshot on the next page). The workflow runtime and the LCS will use additional properties provided by this class during event processing. One such property is the InstanceId, which we need to pass into the base class constructor. The InstanceId is a Globally Unique Identifier (GUID), and every workflow instance the runtime creates receives a unique instance identifier. The InstanceId will allow the runtime to route the events to the proper workflow instance. [ 64 ]
20 Chapter 3 So fa r, we have defined three entities: 1. An IBugFlowService interface, which defines the communications allowed between host and workflows. 2. A Bug class, which holds the data we will pass back and forth. 3. A Bug AddedArgs class, which derives from ExternalDataEventArgs and carries data to the workflow during the BugAdded event. Now, we need to provide a concrete implementation of the service described by the contract. Service Implementation There is nothing particularly special about our local service implementation. The service merely needs to provide an appropriate implementation for the IBugFlowService interface. The service will be an intermediary between the host and bug tracking workflows. public class BugFlowService : IBugFlowService public void AssignBug(Bug bug) // notify someone that it is time to assign a bug. Console.WriteLine(«Assign ‘0’ to a developer», bug.title); public void CreateBug(Guid instanceid, Bug bug) // tell the workflow about the new bug BugAddedArgs args = new BugAddedArgs(instanceID, bug); args.waitforidle = true; EventHandler ev = BugAdded; if (ev!= null) [ 65 ]
21 Sequential Workflows ev(null, args); public event EventHandler BugAdded; In th is example, our AssignBug method only prints a message to the console. In a real application, we might ultimately display a dialog box for a user to assign the bug (and then possibly raise another event to let the workflow know we’ve assigned a bug to a team member). The CreateBug method builds a BugAddedArgs object and raises the BugAdded event. The WaitForIdle property ensures the workflow will be in a state to receive the event. Now that we’ve implemented our messaging service, we can build a workflow to interact with the service. Workflow Implementation In th is scenario, the two key activities from the base activity library are the HandleExternalEventActivity and the CallExternalMethodActivity. In the screenshot below, we’ve placed one of each activity into the workflow to communicate with the host application. The first activity in the workflow is the HandleExternalEventActivity. In the Properties window, we configure the activity to listen for the BugAdded event by first setting Interface Type to our IBugFlowService contract (the full Type name, including namesp ace, is chapter3.ibugflowservice). We can then select BugAdded [ 66 ]
22 Chapter 3 for the EventName proper ty. The Parameters section of the Properties windows allows us to bind the incoming BugAddedArgs parameter to a field in our wo rkflow class. The event handling method uses the typical sender and e parameters to represent incoming event arguments. Clicking the ellipsis in the screen opposite will display a dialog box where we can bind the parameter to an existing public field or property, or create a new field or property. The dialog box is shown below: The next activity is the CallExternalMethod activity. Once again, we will use the Properties window to set the Interface Type property to our IBigFlowService interface, and tell the designer what method we want to invoke by selecting AssignBug for the MethodName. The AssignBug method needs a parameter, so we will click the ellipsis in the tex t box for the bug parameter, and create a new field with the name of _bugtoassign. We are also going to generate an event handler for the activity’s MethodInvoking event. We can do this by placing the cursor inside the text box on the MethodInvoking line and clicking the Generate Handlers link in the bottom of the Properties window. The designer will create an event handler in the class b ehind our workflow. The activity will fire this event just before calling the external method, and inside the event handler we can prepare the data we want to exchange. The screenshot on the next page displays the complete configuration for the CallExternalMethodActivity, which we have named AssignBug. [ 67 ]
23 Sequential Workflows Finally, the following source code is all of the code-behind for our workflow, including the MethodInvoking event handler. Inside the handler, we copy the Bug reference from inside t he _newbug field to the _bugtoassign field. namespace chapter3_sequential public partial class BugFlow : SequentialWorkflowActivity public chapter3.bugaddedargs _newbug; public chapter3.bug _bugtoassign; private void CallAssignBug_MethodInvoking(object sender, EventArgs e) _bugtoassign = _newbug.newbug; Remember the _newbug field is the field where the HandleExternalEventActivity placed the incoming data. We could have also bound the CallExternalMethodActivity parameter directly to the NewBug property of _newbug instead of creating a second field and using a MethodInvoking event handler. [ 68 ]
24 Host Implementation Finally, we need to write a host to execute our workflow. Chapter 3 WorkflowRuntime runtime = new WorkflowRuntime(); runtime.workflowcompleted += new EventHandler ( runtime_workflowcompleted); runtime.workflowterminated += new EventHandler ( runtime_workflowterminated); ExternalDataExchangeService dataservice; dataservice = new ExternalDataExchangeService(); runtime.addservice(dataservice); BugFlow Service bugflow = new BugFlowService(); dataservice.addservice(bugflow); WorkflowInstance instance; instanc e = runtime.createworkflow(typeof(bugflow)); instance.start(); Bug bug = new Bug(1, «Bug Title», «Bug Description»); bugflow.createbug(instance.instanceid, bug); waithandle.waitone(); Here are the steps we are taking in the host program: 1. The host creates an ExternalDataExchangeService and adds the service to the workflow runtime. This service, provided by Windows Workflow, facilitates and manages all local communication services inside the workflow runtime, and will serve as the container for our own BugFlowService. 2. The host creates an instance of the BugFlowService and adds the service to the list of services managed by the data exchange service. The data exchange service will find the IBugFlowService interface, and prepare to handle BugAdded events and AssignBug method calls. 3. The host creates an instance of the BugFlow workflow, and starts the instance running. The workflow instance will wait for a BugAdded event. 4. The host creates a new Bug object, and passes the bug to the bugflow’s CreateBug method. [ 69 ]
25 Sequential Workflows At this point, we will turn to the visual depiction of the proceedings. The CreateBug method in our service will raise the BugAdded event. The workflow runtime (with help from the data exchange service) will catch the event, perform some processing, and pass the event to the workflow instance. The workflow instance might have been waiting for minutes, hours, days, or even months for this event to arrive, so we can’t fire an event directly to the workflow instance. The workflow might not even be in memory at the time we raise the event; the runtime might have persisted the workflow into the database for long-ter m storage. By intercepting the event, the workflow runtime has the chance to load the workflow into memory before passing the event al ong. When the workflow calls the AssignBug method, it takes a direct route to our local service, although workflow does have the opportunity to perform some pre- and post-processing on the method call. Faults Of course, a workflow might not execute flawlessly. Unexpected exceptions may arise because a database server might not be available, for instance. We can also intentionally raise an exception with a ThrowActivity. M anaging exceptions inside a workflow is similar to managing exceptions in Visual Basic or C#. Composite activities can include fault handlers to catch exceptions. If an activity does not handle an exception that occurs, the runtime will let the exception propagate to the parent activity. This is similar to an exception moving up the call stack until the.net runtime can locate an appropriate exception handler. If the runtime does not find a catch handler for a.net application thread, the application terminates. If an exception occurs inside a workflow, and the runtime can find no fault handler to catch the exception, the runtime terminates the workflow and raises the WorkflowTerminated event. [ 70 ]
26 Chapter 3 The FaultHandlerActivity handles exceptions in Windows Workflow. We can view t he fault handlers for a composite activity by right-clicking the activity and selecting View Faults. The designer for a sequential workflow activity includes a shortcut at the bottom of the designer screen. The third small page icon from the left is the view fault shortcut, which will display the screen as shown below: Inside the fault view is a storyboard where we can drop one or more FaultHandlerActivity shapes. We will associate each FaultHandlerActivity with a.net exception type, such as System.NullReferenceException or System. ArgumentException. Just like catch clauses in Visual Basic and C#, a fault handler will handle any exceptions of the given type, or any exceptions derived from the given type. In the following screenshot, overleaf, we’ve added a fault handler with a FaultType of System.Exception. Since every exception ultimately derives from System.Exception, this fault handler will handle any possible exception in the workflow. [ 71 ]
27 Sequential Workflows Eac h fault handler can have a list of child activities to execute when it catches an exception. Just like exception handlers in a general-purpose programming language, the purpose of a fault handler is to clean up or reverse any partially completed work. The workflow runtime will raise the WorkflowCompleted event if the SequentialWorkflowActivity fault handler handles an exception, as opposed to raising the WorkflowTerminated event if the exception goes unhandled. If we want to make sure the workflow terminates, we can use a ThrowActivity inside the fault handler. In the screenshot on the next page, we’ve set up a fault handler with the following configuration: The fault handler specifies a FaultType of System.Exception. The activity binds the incoming exception to a member field with the name of fault. The first child activity, a CodeActivity, will write a message to the console about the exception. The second child activity, a ThrowActivity, will re-throw the handled exception by specifying the Fault as mapping to the fault field. [ 72 ]
28 Chapter 3 Summary In this chapter we’ve explored some of the details of the SequentialWorkflowActivity, including how to communicate and pass data between a sequential workflow and its host, and how to handle faults inside a sequence of activities. Sequential workflows inherit most of their behavior from the SequenceActivity class, which can appear in more than just a sequential workflow. As we will see later in the book, sequential activities are also important in the other primary type of workflow, the event-driven workflow. [ 73 ]