Workflow Activation in (SharePoint) Workflow Manager

Damir Dobric Posts

Next talks:

 

    

Follow me on Twitter: #ddobric



 

 

Archives

If you want to implement the long running business process or in other words to automate the business process, the Windows Workflow Foundation is definitely great choice.
In addition you might probably want to use the host like AppFabric and Workflow Manager. Assuming that you are SharePoint guy or you simply do not want to  use SPC activation (Web Services, Workflow Services and Co.) your way of choice will be Workflow Manager.

Create and Publish

In this case you will implement some workflow. Let’s take a look on following sample workflow:

image
This workflow check input argument value ‘Arg1’ of type string. If it is “stop” it will be terminated. Otherwise it will increment some counter, Assign the Msg-value and set the user status. All this is not for now simulation of some business logic.
To activate this workflow you have to do two things:

1. Publish  the workflow
2. Start the workflow.

In a case of SharePoint you do not have to do these steps. SPS will create and publish the workflow for you. However if you want to do more advanced implementation, you will have to do it by yourself.
Following code snippets shows how to publish the workflow by using of few helper methods.

. . .

     var client = rootClient.CreateChildScope("MyScope", "Host workflow which execute ordered workflows in sequence.");

client.Activities.PublishWorkflow(“WorkflowName”, “C:\\Temp\…\workflow.xaml”);

 

. . .

 

CreateChildScope will create the child scope under the scope defined by ScopePath property of WorkflowManagementClient instance.

  public static WorkflowManagementClient CreateChildScope(this WorkflowManagementClient 
         client,
string scopeName, string comment)
        {
           
var sD =  new ScopeDescription()
                {
                    UserComments =
string.Format(comment, scopeName)
                };

           
return client.CurrentScope.PublishChildScope(scopeName, sD);          
        }
 

One of very important steps in publishing process is translation of expressions. Unfortunately this is required as long .NET framework cannot
perform his on the fly, due missing compiler services, which will come soon.

   public static XElement Translate(string xamlFile)
        {
           
string translatedWorkflowString = null;

           
using (XamlReader xamlReader = new XamlXmlReader(xamlFile))
            {
               
TranslationResults result = ExpressionTranslator.Translate(xamlReader);
               
if (result.Errors.Count == 0)
                {
                   
StringBuilder sb = new StringBuilder();
                   
using (XmlWriter xmlWriter = XmlWriter.Create(sb,
                          
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
                    {
                       
using (XamlXmlWriter writer =
                       
new XamlXmlWriter(xmlWriter, result.Output.SchemaContext))
                        {
                           
XamlServices.Transform(result.Output, writer);
                        }
                    }
                    translatedWorkflowString = sb.ToString();
                }
               
else
                {
                   
throw new InvalidOperationException("Translation errors");
                }
            }

           
return XElement.Parse(translatedWorkflowString);
        }

Finally we can publish the workflow. In this step we will create the workflow on the fly (new WorkflowDescription). The body of this workflow will be
activity whose path is passed in argument xamlFilePath. Note that in this process we do not need any compiler. We simply take a XAML and publish it.
If your workflow references some custom types which are not a part of type system you will have to
extend the trusted surface, before you publish the workflow



public static void PublishWorkflow(this WorkflowManagementClient client,
                                  
string workflowName, string xamlFilePath)
        {
           
// publish the activity description related with the workflow
            client.Activities.Publish(
               
new ActivityDescription(Translate(xamlFilePath)) { Name = workflowName });

           
// now, publish the workflow description
           
WorkflowDescription description = new WorkflowDescription
            {
                Name = workflowName,
                ActivityPath = workflowName,
            };
         

           
// publish!
            client.Workflows.Publish(description);
        }


Start/Activate


If you want to start this workflow, you need to send the activation message to Workflow Manager Front End service. The simplest way to start (activate) the workflow is to send call Start() method.

client.Workflows.Start(m_WorkflowName1);


However this does not give you any flexibility, like input arguments, configuration etc.
For example, let’s assume the workflow has argument: ‘myarg’ of type string.
image
In this case you would start the workflow as follows:

WorkflowStartParameters startParameters = new WorkflowStartParameters();
     
startParameters.Content.Add(
"myarg", "Value1");

string instanceId = client.Workflows.Start(m_WorkflowName1, startParameters);

Sometimes things are getting more complicated. Imagine you are receiving some data and have lot of different workflows which you want to activate based on data.
In his case you can inject the activation filter in workflow during publishing process.

     client.PublishWorkflow(workflowName, @"myworkflow.xaml"
                new MatchAllSubscriptionFilter()
                    {
                        Matches = 
                       
                            {"ActivationProp1Name", “Tenant1”
                        }
                    });


This means the workflow will be activate if Service Bus receives the message with property ActivationProp1Name which has a value “Tenant1”.
To send this message (to start/activate this workflow) you can do following:

client.PublishNotification(new WorkflowNotification()
            {
                Properties =
                {
                    {
"ActivationProp1Name", "Tenant1" }
                },
});

If you didn’t understand the power of this, take a look on next example which publishes lot of different workflows for different tenants.

foreach(var wf in Workflows)    

client.PublishWorkflow(wf.Name, wf.Name
                new MatchAllSubscriptionFilter()
                    {
                        Matches = 
                       
                            {"ActivationProp1Name", wf.Tenant
                        }
                    });

service.Receive(string tenant){

PublishNotification(
new WorkflowNotification()
            {
                Properties =
                {
                    {
"ActivationProp1Name", tenant }
                },
            
});
}

   If the workflow expects input arguments, you can append them to activation message:

client.PublishNotification(new WorkflowNotification()
            {
                Properties =
                {
                    {
"ActivationProp1Name", "Tenant1" }
                },
                Content =
new Dictionary<string, object>()
                {
                    {
"mayarg", “value1” },
                    {
"myarg2", 123 },
                }
            });

POST https://sb17.daenet.lab:12290/myworkflow/$Workflows/Workflow1/$Start HTTP/1.1
User-Agent: Microsoft-WF/1.0.1
Accept: application/xml
Content-Type: application/xml
E2EActivity: i3SI8snqbEShZdcB8Tb7dg==
Authorization: Negotiate ******==
Host: sb17.daenet.lab:12290
Content-Length: 406
Expect: 100-continue

<WorkflowNotification xmlns="http://schemas.microsoft.com/workflow/2012/xaml/activities" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Content i:type="a:ArrayOfKeyValueOfstringanyType" xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"><a:KeyValueOfstringanyType><a:Key>mayarg</a:Key><a:Value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">value1</a:Value></a:KeyValueOfstringanyType><a:KeyValueOfstringanyType><a:Key>myarg2</a:Key><a:Value i:type="b:int" xmlns:b="http://www.w3.org/2001/XMLSchema">123</a:Value></a:KeyValueOfstringanyType></Content><Properties xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"><a:KeyValueOfstringanyType><a:Key>ActivationProp1Name</a:Key><a:Value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">tenant1</a:Value></a:KeyValueOfstringanyType></Properties></WorkflowNotification>

The same data formatted as XML:

image


Posted Jun 06 2013, 08:00 AM by Damir Dobric
developers.de is a .Net Community Blog powered by daenet GmbH.