Feeds:
Posts
Comments

Archive for the ‘Workflow Foundation 4’ Category

The new WF4 version is going to include a lot of enhancements both on the designer side workflow services definition versioning dynamic update…

Let me enumerate a few:

  • C# expressions
  • Errors when connectors not applied.
  • Annotations
  • Auto-connect (drag&Drop)
  • State Machine
  • Multi-assign
  • SQL/State machine activities previously on Codeplex are not include
  • Http Activities (POST/GET/…)
  • Dynamic Update (definition version recognition and update map)
  • Other stuff…

Great job guys!

Advertisements

Read Full Post »

Here at PAT we are developing, among many other things Occhiolino, a new BPM solution that initially will work for our applications, but targeted to be adapted to others by standard means (SOAP) as well as through custom connectors.

We built is using the new Workflow Foundation 4.0 shipped along with the .NET Framework 4.0.

We rehosted both the designer and the workflow runtime.

Designer

On the designer side, we customized the way the property editor behaves on the rehosted environment.

In fact, for a Business Analyst is not easy to write flows using the out-of-the-box behavior. If you need better design time support “for free” (aka Intellisense) you should have the Visual Studio IDE.

We are going to implement our Intellisense support, but this sounds not enough.

image

On the left-down-side we provided a property editor, that allows for each Expression Editor provide a context-specific expression. This is very powerful yet simple to use.

Well, we basically provide many property-specific features there:

  • List of compatible scoped variables and arguments
  • List of possible “expression templates” (where implemented by the provider).

The coolest thing is the this last one… “expression templates”. This is useful when you need to choose from predefined values, –or- when you need to choose from pre-defined expression types.

For instance, let’s take a quick example. Let’s say you have an activity “Change Ticket Status”. This Activity obviously have the property where to store the “Target Status”, right? Now, … in order to take advantage of arguments, we should have an expression editor there. But what IF the Business Analyst (BA) already knows its status and just like to write a constant value there? The BA needs to know the status code… and write it there.

We did better. We provided a “designer feature” that allows our property editor to gather a list of expression templates from the “Ticket Status Property”.

We can also provide a variable-creation logic, which is more simple. By pressing Create New Variable button you come to a “new variable” form where you define the name and choose a compatible type (in this case the type does not have other compatible types so the list is locked):

image

The UI here is MUCH more simpler to use that the out-of-the-box one. Because we add also human-readable description for the types (provided by some designer attributes we apply to types), and filtering on both description and type name.

Workflow Execution Monitor

From the designer console, you can see the workflow queue updated in real time:

image

We also provided a debugger feature that allows the user to display from the designer, the status and the trace of all the execution along with parameters.

image

This was done adding a TrackingPartecipant that stores the history on our own table, that, in turn, is displayed on this page.

Server

We host all our services on our own WCF services. We have one administration service that allows the designer to perform all the “designer/administration” stuff.

We also provide a “Client” service that allows external applications to connect to the “Events” where our Workflow Definitions are attached.

We track versions of the same definition – in order to maintain compatibility with workflows already started – and also we provide Persistence support to all our workflows.

Human Interaction

We provided a metadata-driven set of clients that allows the user to provide data to flows at any stage required by the Business Analyst:

image

All this stuff is interfaced externally to the End-users through:

  • A “traylet” Windows-application
  • A multibrowser lightweight web application where the end-user could provide answer and make the flow continue with the results.

image

We are going to provide in the future, “Human Interaction Clients” for other platforms, such as WP7, IPhone, Android.

I’ll provide further details on the persistence and provider-independence of this solution in a next post!

Keep tuned!

Read Full Post »

For anyone that likes to solve this issue, please look at this post explaining the way to solve it:

Resuming of workflows from InstanceStore in Workflow 4.0

For anyone hosting workflows on a own process (like I am doing – for example on a Windows Service) you should create your own Thread and at given intervals perform the check like this:

I wish to thank Stuart Langley from MS for this useful tip!

   1:   void ResumePendingFlows()
   2:      {
   3:        MInstanceStore store = new MyInstanceStore(); 
   4:        InstanceHandle handle = store.CreateInstanceHandle(null); 
   5:        InstanceOwner owner = store.Execute(handle, new CreateWorkflowOwnerCommand(), 
TimeSpan.MaxValue).InstanceOwner; 
   6:        store.DefaultInstanceOwner = owner; 
   7:        
   8:        bool hasRunnableWorkflows = false; 
   9:        
  10:        foreach (InstancePersistenceEvent currentEvent in store.WaitForEvents(handle, TimeSpan.Zero)) 
  11:        { 
  12:          if (currentEvent == HasRunnableWorkflowEvent.Value) 
  13:          { 
  14:            hasRunnableWorkflows = true; 
  15:            break; 
  16:          } 
  17:        } 
  18:        
  19:        if (hasRunnableWorkflows) 
  20:        { 
  21:          Console.WriteLine("Found runnable workflows");
  22:          WorkflowApplication app = new WorkflowApplication( .... ); 
  23:          app.InstanceStore = store; 
  24:          app.LoadRunnableInstance(); 
  25:          app.Run(); 
  26:        } 
  27:        else 
  28:        { 
  29:          Console.WriteLine("Did not find runnable workflows"); 
  30:        }
  31:      }
 
 
 

Read Full Post »

I’ve found the following best practices watching some videos on Channel9. Thanks to Leon Welicki for sharing these tips:

endpoint.tv – Workflow and Custom Activities – Best Practices

Base Classes:

  1. DO use the designer to declaratively author an activity when you want to define it  visually composing other activities
  2. DO derive from Activity to declaratively author an activity in code when requiring non-designer-supported concepts
    1. ActivityDelegate, child activities, and validation
  1. DO derive from CodeActivity when you want to write imperative code that…
    1. Executes in a single pulse of execution
    2. Does not need to schedule other activities
    3. Does not need advanced WF Runtime features
    4. Does not need to execute async

      4. DO derive from AsyncCodeActivity if the work has to be done asynchronously

  1. DO derive from NativeActivity when you want to write imperative code that…
    1. Executes in multiple pulses – Bookmarks
    2. Schedules other activities
    3. Needs advanced WF Runtime features
  1. DO NOT use NativeActivity to author activities that do not require capabilities of the WF Runtime (use CodeActivity instead)
  2. PREFER using activity composition
  3. Use Code/NativeActivity only when the activity can’t be more easily defined in terms of existing Activities
  4. DO use the derivatives of Activity<TResult> to author activities with a return value

During Runtime:

  1. DO NOT make blocking calls in an activity
  2. DO perform I/O operations off the scheduler thread
    1. Use the asynchronous activity pattern

i. DO NOT do your own thread spawning in your activity (e.g. don’t do Thread.Start)

  1. DO ENSURE that the types of all your arguments and variables are serializable
    1. Exception: type only used in a NoPersistZone
  2. If implementing custom cancelation logic…
    1. DO make sure the instance is left in a consistent state.
    2. DO NOT cancel the activity if the associated work cannot be canceled.
  1. If the operation you are performing is cancelable, DO support cancelation with your activity.

Performance:

  1. CONSIDER overriding CacheMetadata to avoid reflection
  2. CONSIDER caching callback delegates if will be used more than once
  3. CONSIDER limiting variable access (invoke Get only once)
  4. CONSIDER declaring variables as tightly scoped as possible

Activity Design: Properties vs. Arguments vs. Activities

  1. DO use Arguments for expressions that are evaluated once before executing the activity
    1. e.g. If.Condition; WriteLine.Text
  2. DO use Activity<TResult> for expressions that may be evaluated more than once
    1. e.g. While.Condition
    2. DO use CLR Property for things that don’t change for all instances of an activity

i. e.g. InvokeMethod.MethodName

Activity Design: Body vs Children

  1. DO use a Body activity in an activity that schedules a single child (e.g. While, CancellationScope, TransactionScope)
    1. DO add Children-like property if the activity coordinates the execution of its children (e.g. Sequence, Parallel)

i. If it can have one child, it should have one child

Activity Design: Variables

  1. DO add a Variables collection to an activity that needs to enable sharing data between its children (e.g. Sequence, Pick)
  2. DO NOT add a Variables collection in composite activities that do not share data between children (e.g. ForEach)
  3. DO use ImplementationVariables to store internal state in an activity

Activity Design: CacheMetadata

  1. DO override CacheMetadata for…
    1. Adding validation
    2. Registering dynamic arguments / variables
    3. Registering implementation variables
    4. Avoiding reflection

Activity Design – Parallel work

  1. DO schedule several AsyncCodeActivity simultaneously if you need thread parallelism (WF Parallel)
    1. DO NOT do your own thread spawning in your activity (e.g. don’t do Thread.Start)
    2. CONSIDER using an AsyncActivity that leverages TaskParallel (TaskParallel Parallel)

Activity Design:

  1. AVOID Swiss Army knife activity
    1. Simple activities
    2. KISS
    3. E.g. ForEach and ParallelForEach / Sequence and Parallel could be the same with a knob?
  2. DO favor composition over inheritance
  3. DO seal your activities

XAML  Serialization

  1. DO use the Create / Set / Use Pattern for your activity types
  2. CONSIDER using [DefaultValue(null)] to avoid serializing unnecessary properties
  3. CONSIDER using [DependsOn] to control the order of the properties serialization
  4. XAML Serialization
  5. Use [ContentProperty] in composite activity
    1. If it has a Body property, CONSIDER making it the ContentProperty
    2. If it has Child/Children collection, CONSIDER making it the ContentProperty
  6. DO use the Create / Set / Use Pattern for your activity types
  7. CONSIDER use [DefaultValue(null)] to avoid serializing unnecessary properties
  8. CONSIDER use [DependsOn] to control the order of the properties serialization
  9. Use [ContentProperty] in composite activity
    1. If it has a Body property, CONSIDER making it the ContentProperty
    2. If it has Child/Children collection, CONSIDER making it the ContentProperty

Naming

  1. DO NOT add the postfix Activity
  2. DO NOT add the postfix Argument or Arg to Arguments
  3. DO NOT add the postfix Variable or Var to Variables
  4. CONSIDER use the suffix “Scope“ for activities which are designed to decorate other activities and add execution semantics

Execution Properties

  1. CONSIDER using execution properties to share data between ancestor / descendant activities
  2. CONSIDER using execution properties to set ambient properties
  3. DO use well-known constant for execution properties
    1. DO use a name that looks like a fully qualified type name

Validation

  1. DO use RequiredArguments
  2. CONSIDER using OverloadGroups for set of arguments that need to be set together
  3. TIP: DO not assume that a required argument that is required does not yield a null in Execute

Validation – Rules

  1. DO return ERROR when there is no reasonable interpretation for the activity given the settings on the activity or the environment
  2. CONSIDER return a WARNING when there is a reasonable way to interpret the activity but we are not fully confident that the interpretation chosen would be obvious
  3. DO NOT return errors or warning if there is a reasonable interpretation for the activity, e.g. “helpful” usage errors like empty sequences

Hope this helps.

Read Full Post »