Code actions in The Workflow Designer

Code actions in The Workflow Designer

Code actions in The Workflow Designer

Introduction

In version 1.4.3, for integration into the project runtime environment and custom actions execution, the following providers used: IWorkflowRuleProvider and IWorkflowActionProvider. This caused some inconvenience and limited the use of the Designer.

In version 1.4.4, we added the possibility to define actions in The Workflow Designer using C#.

In this article, I will explain:

  • How to create user's actions in the Designer
  • How to execute user's actions
  • How to transmit parameters between Actions

We will base ourselves on the example from the article Alternative to Windows Workflow Foundation.

Code actions

It's new functionality. You can read description here.

Prepare a solution

We need to change the base example from the previous article. Add a document type request to the CreateInstance method of the WorkflowConsole project:

private static void CreateInstance()
{
    processId = Guid.NewGuid();
    Console.Write("Please, enter type of document:");
    processType = Console.ReadLine();

    try
    {
        WorkflowApp.WorkflowInit.Runtime.CreateInstance(schemeCode, processId.Value);
        Console.WriteLine("CreateInstance - OK.", processId);
    }
    catch (Exception ex) {
        Console.WriteLine("CreateInstance - Exception: {0}", ex.Message);
        processId = null;
        processType = null;
    }
}

The document type will be stored in the processType variable. The variable is public, so we can get to it from WFE. We add the document type to Console:

//...
public static string processType = null;
static void Main(string[] args)
{
//...
    do
    {
        if (processId.HasValue)
        {
            Console.WriteLine("ProcessId = '{0}' (Type:{1}). CurrentState: {2}, CurrentActivity: {3}",
                processId,
                processType,
                WorkflowInit.Runtime.GetCurrentStateName(processId.Value),
                WorkflowInit.Runtime.GetCurrentActivityName(processId.Value));
        }
        
        Console.Write("Enter code of operation:");
        char operation = Console.ReadLine().FirstOrDefault();
        //...
    }
}

Create a scheme

Activities:

  • draft - starting state
  • switchtype - pending state
  • state_contract - a state for "Contract" type
  • state_invoce - a state for "Invoice" type
  • state_unknown - a state for unknown document type
  • final - final state

In switchtype state, the text "Hello World!" will be entered to Console.
Next is automatic transfer to one of three states:

  • If the processType is equal to "Contract" the state will be "state_contract"
  • If "Invoice" then "state-invoice"
  • Otherwise, "state_unknown"

In state_contract, state-invoice, and state_unknown states, call a method of e-mail sending.

Let's click the "Code Actions" icon in the toolbar and add Code actions:

  • HelloWorld
  • CheckType
  • SendMail

For "HelloWorld" item click the "Edit code" button; in the window we insert:

Console.WriteLine("Hello world!");

For "CheckType" item click the "Edit code" button; in the window we insert:

var a = WorkflowAppConsole.Program.processType ?? string.Empty;
var b = parameter ?? string.Empty;

return a.ToLower() == b.ToLower();

For "SendMail" item click the "Edit code" button; in the window we insert:

try
{
    SmtpClient Smtp = new SmtpClient("smtp.mail.com", 25);
    Smtp.Credentials = new NetworkCredential("login", "pass");
    
    MailMessage Message = new MailMessage();
    Message.From = new MailAddress("from@mail.com");
    Message.To.Add(new MailAddress("to@mail.com"));
    Message.Subject = "WorkflowEngine.NET";
    Message.Body = "Parameter:" + parameter ?? string.Empty;
    Smtp.Send(Message);
    Console.WriteLine("SendMail - OK. Body:" + Message.Body);
}
catch(Exception ex)
{
    Console.WriteLine("SendMail error:" + ex.Message);
}

Ok, will add call the Code actions in activities and transitions.

In "switchtype" activity add "HelloWorld" action in Implemetation block:

For each out-Transition from switchtype activity set Condition block (Type="Action", Action="CheckType") and set Action parameter Contract or Invoice:

In "state_contract", "state_invoice", "state_unknown" activities add "SendMail" action in Implemetation block:

Click on the "Save" button for save the schema to DB.

In the result, you should receive the following scheme: scheme.xml.

Test

Execute the WorkflowAppConsole.

Operation:
0 - CreateInstance
1 - GetAvailableCommands
2 - ExecuteCommand
3 - GetAvailableState
4 - SetState
5 - DeleteProcess
9 - Exit
The process isn`t created.
Please, enter type of document:Contract
CreateInstance - OK.
ProcessId = '79067ecd-01e0-4bf1-9b96-6d6a369ea7bc' (Type:Contract). CurrentState: , CurrentActivity: draft
Enter code of operation:2
Available commands:
- agree (LocalizedName:agree, Classifier:Direct)
Enter command:agree
Hello world!
ExecuteCommand - OK.

ProcessId = '79067ecd-01e0-4bf1-9b96-6d6a369ea7bc' (Type:Contract). CurrentState: , CurrentActivity: state_contract
Enter code of operation:2
Available commands:
- agree (LocalizedName:agree, Classifier:Direct)
Enter command:agree
SendMail - OK. Body:Parameter:Contract
ExecuteCommand - OK.

ProcessId = '79067ecd-01e0-4bf1-9b96-6d6a369ea7bc' (Type:Contract). CurrentState: final, CurrentActivity: final
Enter code of operation:0
Please, enter type of document:Invoice
CreateInstance - OK.

ProcessId = 'ce6f472d-5d62-4a32-afde-6bb5c5d3fdae' (Type:Invoice). CurrentState: , CurrentActivity: draft
Enter code of operation:2
Available commands:
- agree (LocalizedName:agree, Classifier:Direct)
Enter command:agree
Hello world!
ExecuteCommand - OK.

ProcessId = 'ce6f472d-5d62-4a32-afde-6bb5c5d3fdae' (Type:Invoice). CurrentState: , CurrentActivity: state_invoce
Enter code of operation:2
Available commands:
- agree (LocalizedName:agree, Classifier:Direct)
Enter command:agree
SendMail - OK. Body:Parameter:Invoice
ExecuteCommand - OK.

ProcessId = 'ce6f472d-5d62-4a32-afde-6bb5c5d3fdae' (Type:Invoice). CurrentState: final, CurrentActivity: final
Enter code of operation:0
Please, enter type of document:Other
CreateInstance - OK.

ProcessId = 'c228863c-1035-4784-8d6b-edd321845a57' (Type:Other). CurrentState: , CurrentActivity: draft
Enter code of operation:2
Available commands:
- agree (LocalizedName:agree, Classifier:Direct)
Enter command:agree
Hello world!
SendMail - OK. Body:Parameter:
ExecuteCommand - OK.

ProcessId = 'c228863c-1035-4784-8d6b-edd321845a57' (Type:Other). CurrentState: , CurrentActivity: state_unknown
Enter code of operation:2
Available commands:
- agree (LocalizedName:agree, Classifier:Direct)
Enter command:agree
ExecuteCommand - OK.

ProcessId = 'c228863c-1035-4784-8d6b-edd321845a57' (Type:Other). CurrentState: final, CurrentActivity: final

Parameters transition between Actions

For each ProcessInstance, the engine stores a set of variables. You can use the Parameters block for parameter transition between CodeActions.

The following parameter types are supported:

  1. Temporary - the parameter retains its value during the execution of command and is not
  2. stored in the database (persistence store)
  3. Persistence - the parameter always retains its value and is stored in the database (persistence store)
  4. System - the parameter is part of the workflow engine

If the parameter is used within one Activity, it is necessary to use Temporary type, if the parameter will be used for different activities, Persistence type.

Add persistence parameter "TestParam"

Let's try to use this function. For setting parameters, use the SetParameter function, and to get parameters, GetParameter.

Add the Code action "SetParameterValue" which will record the text value in the "TestParam" parameter.

if (!processInstance.ProcessParameters.Any(c => c.Name == "TestParam"))
{
    Console.WriteLine("TestParam isn`t found!");
    return;
}
processInstance.SetParameter<string>("TestParam", parameter);
Console.WriteLine("TestParam set value '{0}'", parameter);

Add the Code action "ShowParameterValue", which will output the parameter value to the "TestParam" console.

if (processInstance.ProcessParameters.Any(c => c.Name == "TestParam"))
{
    var val = processInstance.GetParameter<string>("TestParam");
    Console.WriteLine("TestParam: '{0}'", val);
}
else{
    Console.WriteLine("TestParam isn`t found!");
}

Let's create a 4-stage scheme. On the first stage, we will request a parameter, on the second, output it.

In Activity_1 set SetParameter in Implementation block:

In Activity_2 set ShowParameter in Implementation block:

Click on the "Save" button for save the schema to DB.

In the result, you should receive the following scheme: scheme2.xml.

Test

Operation:
0 - CreateInstance
1 - GetAvailableCommands
2 - ExecuteCommand
3 - GetAvailableState
4 - SetState
5 - DeleteProcess
9 - Exit
The process isn`t created.
Please, enter type of document:Invoice
CreateInstance - OK.
ProcessId = 'a3473491-37b4-41b9-ba13-517848f6e101' (Type:Invoice). CurrentState: , CurrentActivity: draft
Enter code of operation:2
Available commands:
- agree (LocalizedName:agree, Classifier:Direct)
Enter command:agree
TestParam set value 'Hello World'
ExecuteCommand - OK.

ProcessId = 'a3473491-37b4-41b9-ba13-517848f6e101' (Type:Invoice). CurrentState: , CurrentActivity: Activity_1
Enter code of operation:2
Available commands:
- agree (LocalizedName:agree, Classifier:Direct)
Enter command:agree
TestParam: 'Hello World'
ExecuteCommand - OK.

ProcessId = 'a3473491-37b4-41b9-ba13-517848f6e101' (Type:Invoice). CurrentState: , CurrentActivity: Activity_2

Security

The use of Code actions can be unsafe. For critically important applications, we recommend using the following providers: IWorkflowActionProvider and IWorkflowRuleProvider.

To switch Code actions off, use the DisableCodeActions method in WorkflowRuntime.

Total

The new functionality broadens WFE use, so now you can create various actions in the designer without recompilation of the application project. This will accelerate design and adjustment.By integrating WFE into your project, you will bring your application level closer to a BPM solutions level.

In version 1.4.4 we implemented only C#. If you need other language support, please write to us.

  • By Dmitry Melnikov
  • 2/16/2015
  • code actions, user action, workflow designer