Showing posts with label WF. Show all posts
Showing posts with label WF. Show all posts

Tuesday, July 7, 2009

WF runtime

public class WorkflowRuntime { public WorkflowRuntime(); public void AddService(object service); public void RemoveService(object service); public void StartRuntime(); public void StopRuntime(); public WorkflowInstance CreateWorkflow(XmlReader reader); public WorkflowInstance GetWorkflow(Guid instanceId); /* *** other members *** */ } public sealed class WorkflowInstance { public Guid InstanceId { get; } public void Start(); public void Load(); public void Unload(); public void EnqueueItem(IComparable queueName, object item, IPendingWork pendingWork, object workItem); /* *** other members *** */ } class Program { static void Main() { using(WorkflowRuntime runtime = new WorkflowRuntime()) { TypeProvider typeProvider = new TypeProvider(runtime); typeProvider.AddAssemblyReference("EssentialWF.dll"); runtime.AddService(typeProvider); runtime.StartRuntime(); WorkflowInstance instance = null; using (XmlTextReader reader = new XmlTextReader("OpenSesame.xoml")) { instance = runtime.CreateWorkflow(reader); instance.Start(); } string s = Console.ReadLine(); instance.EnqueueItem("r1", s, null, null); // Prevent Main from exiting before // the WF program instance completes Console.ReadLine(); runtime.StopRuntime(); } } }

When the Start method is called on the WorkflowInstance, the WF runtime runs the WF program asynchronously. But other threading models are supported by the WF runtime. When a ReadLine activity executes, it creates a WF program queue. When our console application (which is playing the role of a listener) reads a string from the console, it resumes the execution of the bookmark established by the ReadLine by enqueuing the string. The name of the WF program queue is the same name, "r1", that we gave to the ReadLine activity (per the execution logic of ReadLine).

In order to illustrate the mechanics of passivation, we can write two different console applications. The first one begins the execution of an instance of the Open, Sesame program.

class FirstProgram { static string ConnectionString = "Initial Catalog=SqlPersistenceService;Data Source=localhost;Integrated Security=SSPI;"; static void Main() { using (WorkflowRuntime runtime = new WorkflowRuntime()) { SqlWorkflowPersistenceService persistenceService = new SqlWorkflowPersistenceService(ConnectionString); runtime.AddService(persistenceService); TypeProvider typeProvider = new TypeProvider(runtime); typeProvider.AddAssemblyReference("EssentialWF.dll"); runtime.AddService(typeProvider); runtime.StartRuntime(); WorkflowInstance instance = null; using (XmlTextReader reader = new XmlTextReader("OpenSesame.xoml")) { instance = runtime.CreateWorkflow(reader); instance.Start(); } Guid durableHandle = instance.InstanceId; // save the Guid... instance.Unload(); runtime.StopRuntime(); } } }

The WF program instance never completes because it is expecting to receive a string after it prints the key, and we do not provide it with any input. When the WorkflowInstance.Unload method is called,[2] the instance is passivated. Inspection of the SQL Server database table that holds passivated WF program instances will show us a row representing the idle Open, Sesame program instance.

In order to resume the passivated instance in another CLR application domain, we need to have some way of identifying the instance. That is precisely the purpose of the InstanceId property of WorkflowInstance. This globally unique identifier can be saved and then later passed as a parameter to the WorkflowRuntime.GetWorkflow method in order to obtain a fresh WorkflowInstance for the WF program instance carrying that identifier.

class SecondProgram { static string ConnectionString = "Initial Catalog=SqlPersistenceService;Data Source=localhost;Integrated Security=SSPI;"; static void Main() { using (WorkflowRuntime runtime = new WorkflowRuntime()) { SqlWorkflowPersistenceService persistenceService = new SqlWorkflowPersistenceService(ConnectionString); runtime.AddService(persistenceService); TypeProvider typeProvider = new TypeProvider(runtime); typeProvider.AddAssemblyReference("EssentialWF.dll"); runtime.AddService(typeProvider); runtime.StartRuntime(); // get the identifier we had saved Guid id = "saveed from first program"; WorkflowInstance instance = runtime.GetWorkflow(id); // user must enter the key that was printed // during the execution of the first part of // the Open, Sesame program string s = Console.ReadLine(); instance.EnqueueItem("r1", s, null, null); // Prevent Main from exiting before // the WF program instance completes Console.ReadLine(); runtime.StopRuntime(); } } }

The passivated (bookmarked) WF program instance picks up where it left off, and writes its result to the console after we provide the second string.

WF Programming Model

Workflow is queue and scheduler.

public class ReadLine : Activity { private string text; public string Text { get { return text; } } protected override ActivityExecutionStatus Execute( ActivityExecutionContext context) { WorkflowQueuingService qService = context.GetService<WorkflowQueuingService>(); WorkflowQueue queue = qService.CreateWorkflowQueue(this.Name, true); queue.QueueItemAvailable += this.ContinueAt; return ActivityExecutionStatus.Executing; } void ContinueAt(object sender, QueueEventArgs e) { ActivityExecutionContext context = sender as ActivityExecutionContext; WorkflowQueuingService qService = context.GetService<WorkflowQueuingService>(); WorkflowQueue queue = qService.GetWorkflowQueue(this.Name); text = (string) queue.Dequeue(); qService.DeleteWorkflowQueue(this.Name); context.CloseActivity(); } }

In WF, the data structure chosen to represent a bookmark's capacity to hold data is queue. This queue, which we shall call a WF program queue is created by ReadLine using WorkflowQueuingService.

namespace System.Workflow.Runtime { public class WorkflowQueuingService { // queueName is the bookmark name public WorkflowQueue CreateWorkflowQueue( IComparable queueName, bool transactional); public bool Exists(IComparable queueName); public WorkflowQueue GetWorkflowQueue(IComparable queueName); public void DeleteWorkflowQueue(IComparable queueName); /* *** other members *** */ } }

The WorkflowQueue object that is returned by the CreateWorkflowQueue method offers an event, QueueItemAvailable. Despite the syntactic sugar of the C# event, this event represents the asynchronous delivery of stimulus from an external entity to an activity, and is exactly the same pattern of bookmark resumption. The more refined WF version of the programming model for bookmarks allows a bookmark's payload (a WF program queue) to hold an ordered list of inputs that await processing (instead of a single object as did the bookmark in Chapter 1). The physical resumption point of the bookmark is still just a delegate (ContinueAt) even though in the WF programming model the delegate is indicated using the += event subscription syntax of C#.

namespace System.Workflow.Runtime { public class WorkflowQueue { public event EventHandler<QueueEventArgs> QueueItemAvailable; public object Dequeue(); public int Count { get; } public IComparable QueueName { get; } /* *** other members *** */ } }

The return value of the ReadLine activity's Execute method indicates that, at that point in time, the ReadLine has pending bookmarks; its execution is not complete. When an item is enqueued in its WF program queue, perhaps days after the ReadLine began its execution, the bookmark is resumed and, as a result, the ContinueAt method is invoked. After obtaining the item from its queue and setting the value of its text field, the ReadLine activity reports its completion.

public class Sequence : CompositeActivity { protected override ActivityExecutionStatus Execute( ActivityExecutionContext context) { if (this.EnabledActivities.Count == 0) return ActivityExecutionStatus.Closed; Activity child = this.EnabledActivities[0]; child.Closed += this.ContinueAt; context.ExecuteActivity(child); return ActivityExecutionStatus.Executing; } void ContinueAt(object sender, ActivityExecutionStatusChangedEventArgs e) { ActivityExecutionContext context = sender as ActivityExecutionContext; e.Activity.Closed -= this.ContinueAt; int index = this.EnabledActivities.IndexOf(e.Activity); if ((index + 1) == this.EnabledActivities.Count) context.CloseActivity(); else { Activity child = this.EnabledActivities[index + 1]; child.Closed += this.ContinueAt; context.ExecuteActivity(child); } } }

Sequence cannot directly execute its child activities since the Activity.Execute method has accessibility of protected internal. Instead, Sequence requests the execution of a child activity via ActivityExecutionContext.

Sequence subscribes to the Activity.Closed event before it requests the execution of a child activity. When the child activity completes its execution, the execution of the Sequence is resumed at the ContinueAt method. The Sequence activity's subscription to the Closed event of a child activity is syntactic sugar for the creation of a bookmark that is managed internally, on behalf of Sequence, by the WF runtime.

The ActivityExecutionContext type is effectively an activity-facing abstraction on top of the WF runtime.

namespace System.Workflow.ComponentModel { public class ActivityExecutionContext : System.IServiceProvider { public void ExecuteActivity(Activity activity); public void CloseActivity(); public T GetService<T>(); public object GetService(Type serviceType); /* *** other members *** */ } }

Sunday, July 5, 2009

When workflow is persisted.

If a persistence service is loaded, the state of the workflow is persisted in the following situations:

  1. When a workflow becomes idle. For example, when a workflow is waiting for an external event or executes a DelayActivity. To persist and unload a workflow when it becomes idle, the service must return true from the UnloadOnIdle method. With the standard, SqlWorkflowPersistenceService, you can affect this behavior by setting an UnloadOnIdle parameter during the construction of the service.
  2. When a workflow completes or terminates.
  3. When a TransactionScopeActivity (or CompensatableTransactionScopeActivity) completes. A TransactionScopeScopeActivity identifies a logical unit of work that is ended when the activity completes.
  4. When a CompenstableSequneceActivity completes. A CompensatableSequenceActivity identifies a set of child activities that a compensatable. Compensation is the ability to undo the actions of a completed activity.
  5. When a custom activity that is decorated with the PersistOnCloseAttribute completes.
  6. When you manually invoke one of the methods on a WorkflowInstance that cause a persistence operation. Examples are Unload and TryUnload. The Load method results in a previously unloaded and persisted workflow being retrieved and loaded back into memory.

It is important to make a distinction between saving a workflow and saving the state of a workflow. Not all persistence operations result in a new serialized copy of a workflow being saved. For instance, when a workflow completes or terminates, the standard SQL Server persistence service (SqlWorkflowPersistenceService) actually removes the persisted copy of the workflow. It persisted the workflow in the sense that it updated the durable store with the state of the workflow. If you implement your own persistence service, you may choose to do something else when a workflow completes.

Why ManualWorkflowSchedulerService should be used in asp.net enviroment

Why ManualWorkflowSchedulerService should be used in asp.net enviroment

Thursday, June 25, 2009

Cancel Hanlder

The cancel handler view provides a way to define activity cancellation logic. A cancel handler has some similarities to the fault handlers just discussed. Like fault handlers, they are attached only to a composite activity.However, cancel handlers don’t catch and handle exceptions. Instead, they specify the cleanup actions that take place when an executing activity is canceled.

actions that take place when an executing activity is canceled. The need for cancel handlers is best illustrated with an example: A ListenActivity is a composite activity that allows you to define multiple child activities under it. Assume that each child of the ListenActivity is a HandleExternalEventActivity that listens for a different external event. With this scenario, each HandleExternalEventActivity is executing at the same time waiting for an event. Only one of these children will eventually receive its event and complete execution. The other sibling activities will be canceled by the ListenActivity parent. By entering a cancel handler for the parent ListenActivity, you define the steps to execute when the incomplete children are canceled. You won’t always need a cancel handler. But if your activities require any cleanup when they are canceled, a cancel handler is an appropriate place to define that logic.

Tuesday, June 23, 2009

ActivityExecutionContext

  1. It is a container of services that is availabe to activities ruing their execution. This set of service is the same for all activities in all WF program instances. Some services are provided by the WF runtime and are always obtainable from AEC. Custom services can be offered by the application that hosts the WF runtime; such services are made available to activities by using the AddService method of WorkflowRuntime. class ReadLine:Activity { private string text; public string Text { get { return text; } } protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) { WorkflowQueuingService qService = executionContext.GetService<WorkflowQueuingService>(); WorkflowQueue queue = qService.CreateWorkflowQueue(this.Name, true); queue.QueueItemAvailable += ContinueAt; return ActivityExecutionStatus.Executing; } void ContinueAt(object sender, QueueEventArgs e) { ActivityExecutionContext context = sender as ActivityExecutionContext; WorkflowQueuingService qService = context.GetService<WorkflowQueuingService>(); WorkflowQueue queue = qService.GetWorkflowQueue(this.Name); text = (string)queue.Dequeue(); qService.DeleteWorkflowQueue(this.Name); context.CloseActivity(); } }
  2. ActivityExecutionContext is as an API surface through which activities can interact with the (internal) scheduler component of the WF runtime. For example, the ExecuteActivity method requests that a work queue. The CloseActivity method requests that the WF runtime finalize the current activity's transition to the Closed state, and resume the internal bookmark that notifies the parent composite activity of the activity's completion. AEC therefore abstracts the internal machinery of the WF runtime; even though we have explained the execution model of the WF runtime in terms of a scheduler and a work queue, these entities are not represented directly in the public API of the WF programming model. public class Sequence : CompositeActivity { protected override ActivityExecutionStatus Execute(ActivityExecutionContext context) { if (this.EnabledActivities.Count == 0) return ActivityExecutionStatus.Closed; Activity child = this.EnabledActivities[0]; child.Closed += this.ContinueAt; context.ExecuteActivity(child); return ActivityExecutionStatus.Executing; } void ContinueAt(object sender, ActivityExecutionStatusChangedEventArgs e) { ActivityExecutionContext context = sender as ActivityExecutionContext; e.Activity.Closed -= this.ContinueAt; int index = this.EnabledActivities.IndexOf(e.Activity); if ((index + 1) == this.EnabledActivities.Count) { context.CloseActivity(); } else { Activity child = this.EnabledActivities[index + 1]; child.Closed += this.ContinueAt; context.ExecuteActivity(child); } } }
  3. The execution of a WF program instance is episodic, and at the end of each episode when the WF program instance becomes idle, the instance can be persisted in durable storage as continuation. This continuation, because it represents the entirety of the program instance's state that is necessary for resuming its execution, holds the relevant (internal) WF runtime execution state plus user-defined state, sometimes called the application state. The application state is nothing but the WF program instance's tree of activities (the actual CLR objects), which are usually stateful entities. The runtime state includes the state of the scheduler work queue, WF program queues, and bookkeeping information about internally managed bookmarks (such as subscriptions to the Activity.Closed event).

    The resumption point of a bookmark is called an execution handler, so we can refer to the (heap-allocated) execution state required by an execution handler as its execution context. Because an execution handler is typically a method on an activity, we will often refer to this execution context as activity execution context.

    ActivityExecutionContext is a programmatic abstraction for precisely this execution context. ActivityExecutionContext is passed to every execution handler either as an explicit argument (as for Activity.Execute) or as the sender parameter in the case of execution handlers that conform to a standard .NET Framework event handler delegate type.

Tuesday, December 9, 2008

ManualWorkflowSchedulerService

By default, workflow runtime use DefaultWorkflowSchedulerService as scheduler, what this means is that the workflow instance run in a separate thread. So if you want to wait for the workflow to finished, you need AutoResetEvent object to synchronize the thread. But in asp.net you make want to use a single thread. Here is an article Using Workflows with ASP.NET.

//you need to add this to to runtime initialization ManualWorkflowSchedulerService manualService = new ManualWorkflowSchedulerService(); _workflowRuntime.AddService(manualService); //you need to perform additional steps //to run the workflow. WorkflowInstance instance = _workflowRuntime.CreateWorkflow( typeof(SimpleCalculatorWorkflow.Workflow1), wfArguments); instance.Start(); ManualWorkflowSchedulerService manualScheduler = _workflowRuntime.GetService(typeof(ManualWorkflowSchedulerService)) as ManualWorkflowSchedulerService; manualScheduler.RunWorkflow(instance.InstanceId);

WF runtime engine

The workflow runtime is just a class, you create a instance of the class in your application, make it a static or global, so the instance live through all the live of your application, in this case, your application is hosting the workflow runtime. In fact your application can create more than one runtime. However, you typically won't need to do that. A single instance of the workflow runtime is capable of managing the execution of multiple workflow instances. One possible reason to create multiple runtime instances in a single appDomain would be if each instance required a different set of conflicting runtime services or configuration settings. You can also extend the WorkflowRuntme class to initialize the runtime engine.

Beginning with .NET 3.5, Microsoft has added the ability to expose workflow instances as WCF services. To implement this, they developed a hybrid workflow runtime class named WorkflowServiceHost (found in the System.ServiceModel namespace and packaged in the System. WorkflowServices assembly). This class combines the basic capabilities of WorkflowRuntime (it hosts workflow instances) with ServiceHost (a WCF class that exposes services to clients). Use WorkflowServiceHost when you implement your own service host application instead of using Internet Information Services (IIS) or Windows Activation Services (WAS).

The runtime engine provides an execution environment for your workflows. You don’t directly execute workflows within your application. Instead, you ask the runtime engine to create an instance of a workflow, which you then instruct to start.

By default, workflows execute asynchronously in a thread that is managed by the runtime engine. This allows you to start multiple workflows from your host application at the same time, with all of them under the control of the runtime engine.

Each workflow can go through multiple execution states throughout its lifetime. For example, all workflows start in the created state and then move into the running state when execution begins. The workflow can also pass into states such as suspended, terminated, or completed. Other events associated with a workflow such as idled, persisted, loaded, or unloaded are possible. It is the runtime engine that manages the life and death of each workflow as it passes through these states.

The runtime engine is also responsible for scheduling and managing execution threads, workflow persistence, workflow transactions (committing of batched work), and workflow tracking. However, while the responsibility for these tasks rests with the runtime engine, it doesn’t actually handle these duties by itself. Each of these tasks has been implemented as a runtime service that you create and register with the runtime engine during application startup. This modular design permits you to swap out a default implementation in favor of one that you’ve developed.

Included in the runtime engine is a flexible rules evaluation engine. This engine is able to evaluate simple rule conditions such as those that you add to an IfElseActivity or WhileActivity. Or it can handle a complex set of rules (called a RuleSet) that you specify within a PolicyActivity. A RuleSet defines multiple rules that are evaluated in sequence and automatically reevaluated based on the actions of rules later in the set.

The workflow runtime engine also exposes several public events that can be handled by the host application. These events allow you to directly monitor the status of each workflow as it passes between execution states. Each event carries with it an instance of the WorkflowInstance class. This class acts as a proxy to the real workflow instance that is managed by the runtime engine.

Get the output from a workflow

The output of the workflow instance is can be obtained by the OutputParameters like the following.

_workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) { _result = (Double)e.OutputParameters["Result"]; _waitHandle.Set(); };

Debugger WF

To debug a workflow, you need to set the workflow's host project to be the startup project. If this project is class library project, you need to set the default start action, so that the start action trigger a process.

Workflow are different

Workflows represent a different programming model. It’s a model that promotes a clear separation between what to do and when to do it. This separation allows you to change the when without affecting the what. Workflows generally use a declarative programming model rather than a procedural one. With this model, business logic can be encapsulated in discrete components. But the rules that govern the flow of control between components are declarative.

Friday, November 21, 2008

EventHandlingScopeActivity

The EventHandlingScopeActivity is a dramatically different type of event handling activity. It has two discrete sections: a main line child activity and a set of event handling activities. The single main line child activity executes in a normal manner. When the main line activity completes, the entire EventHandlingScopeActivity comes to an end. In addition to the main line child activity, event handling activites are contained within a single EventHandlersActivity that is a child of EventHandlingScopeActivity. The EventHandlerActivity is the parent for one or more EventDrivenActivity instances, with each one activing as a seperate branch of execution. Each EventDrivenActivity contains one or more child activities, but the first child must implement IEventActivity(e.g. HandlingExternalEventActivity) in order to start execution of the branch. When an event is received, the activites within EventDrivenActivty are executed. However, unlike the more common event handling of ListenActivity, the order event branch are all still alive. They can also receive their events and execute the activities within their execution branch. The orginal branch that recived its event cal even receive it again.

These activities are used in advanced scenarios where you need to concurrently handle multiple events and execute a main line set of activities at the same time. In the example that follows, you will implement a workflow using the EventHandlingScopeActivity. It contains a main line set of activities that execute while a condition is true. It also handles three external events, one of which is being used to set the condition for the main line activities.