Skip to main content

Introducing Formengine - The New Formbuilder, try for FREE formengine.io.

Conditional Transitions. Conditions and Expressions

General information

Workflow processes often use conditional transitions; in this section, we discuss Conditional Transitions in the Workflow Engine. This guide is also 100% suitable for Workflow Server.

In the process scheme, the transitions are specified with at least one condition per each transition, which determines whether this transition can be executed.

Condition1

A condition can have one of the following types:

  • Always - unconditional transition, to be always executed.
  • Action - an Action of the Condition type (hereinafter, Action-Condition); it is a function that returns true or false, called when the Workflow Engine determines whether to make a transition or not. In other words, this is the 'if' expression.
  • Expression - a simple expression that returns true or false. Using special syntax, you can directly apply the values of the process parameters in it. In other words, this is the 'if' expression.
  • Otherwise - executed only if other transitions cannot be completed, that is, if Always or their conditions have returned false. In other words, this is the 'else' expression.

A transition can have exactly one condition of the Always type or the Otherwise type; and, any number of conditions of the Action -Condition type or the Expression type.

Condition2

  1. A transition can be specified by any number of Action-Condition or Expression conditions.
  2. The result returned by Action-Condition or Expression can be inverted.
  3. PreExec. Result is the value used in the PreExecution mode only.
  4. By default, all conditions are combined by the AND operator; but, you can change it to OR.

During the runtime, when selecting transitions towards the next state, the Workflow Engine uses the following priorities.

  • If Always exists, it is executed in any case.
  • Each of Action-Condition and Expression are executed in turn. If they return true, the transition is performed.
  • Otherwise is executed only if no transitions have been performed at the previous two stages.

Now, we consider all the ways to create an Action-Condition or an Expression.

Expressions

An expression returns true or false. Its main advantage is that you can use the process parameters in it. Any reference to a process parameter begins with the '@' character. An expression to substitute a parameter can be as follows:

  • @ParameterName - the easiest option, the value of the parameter named 'parameterName' is substituted from the process. The type is the same as the type the parameter value.
  • @ObjectParameter.ObjectProperty.StringProperty - partial parameters can be used in the expression. That is, if you have a complex, multi-level object stored in the parameter, you can substitute a part of this object.
  • @ParameterName:formatString - the expression with a format string. A string formatted according to the formatString template is substituted using the format syntax for the .NET platform. Please, for more details see here.
  • @(ParameterName:formatString) - if different special characters and delimiters are used in the format string, you must additionally enclose the expression in round brackets.
  • @(ParameterName).Contains("Substring") - by enclosing the expression in round brackets, you can use the methods of the parameter object. In this example, the Contains method of the string is used.

In addition to substitutions, you can use the following signs in expressions:

  • operators and,AND,And,&,&& - AND condition.
  • operators or,OR,Or,|,|| - OR condition.
  • operators =,== - equality.
  • operators <>,!= - inequality.
  • operators not,NOT,Not,! - negation.
  • operators >,<,>=,<= - comparison.
  • round brackets.
  • operators of addition, subtraction, multiplication, division, etc.
  • any other elements of lambda expressions in C#, see here for more details.

Examples of expressions

  • @Amount > 100 - the process parameter named 'Amount' is used in the expression.
  • @Document.Amount > 100 AND @Document.CreationDate <= DateTime.Now.AddDays(-1) - you see that some C# types and functions can be used. The process parameter called 'Document', containing an object with the 'Amount' and 'CreationDate' properties, is used in the expression.
  • NOT @Document.IsSigned - The process parameter called 'Document', containing an object with the boolean property 'IsSigned', is used in the expression.
  • @(Document.CreationDate:yyyy) = "2020" - an example with a format string; a format string turns the parameter into a string, so you need to compare it with a string too.

Let us consider how Workflow Engine interprets expressions to clearly understand the subject:

The following expression:

@Document.Amount > 100 AND @Document.CreationDate <= DateTime.Now.AddDays(-1)

It turns into this asynchronous lambda:

async (processInstance) => (await processInstance.GetParameterAsync<dynamic> ("Document.Amount")) > 100 && (await processInstance.GetParameterAsync<dynamic> ("Document.CreationDate")) <= DateTime.Now.AddDays(-1);

The resulting asynchronous lambda is compiled once, the first time you access the process scheme. Then, the already cached compiled function is used, therefore, expressions do not affect the performance.

You can use the designer to test if your expression can be compiled.

Condition6

After clicking on 'Validate', the compilation result window will appear.

Condition7

How to create Action-Conditions

Option One, Using IWorkflowActionProvider

How to work with Workflow Action Provider, please, read in detail in this guide. First of all, we should create a class, implementing IWorkflowActionProvider.

public interface IWorkflowActionProvider
{
void ExecuteAction(string name, ProcessInstance processInstance, WorkflowRuntime runtime,
string actionParameter);

Task ExecuteActionAsync(string name, ProcessInstance processInstance, WorkflowRuntime runtime,
string actionParameter, CancellationToken token);

bool ExecuteCondition(string name, ProcessInstance processInstance, WorkflowRuntime runtime,
string actionParameter);

Task<bool> ExecuteConditionAsync(string name, ProcessInstance processInstance, WorkflowRuntime runtime,
string actionParameter, CancellationToken token);

bool IsActionAsync(string name, string schemeCode);

bool IsConditionAsync(string name, string schemeCode);

List<string> GetActions(string schemeCode);

List<string> GetConditions(string schemeCode);
}

The ExecuteAction, ExecuteActionAsync, IsActionAsync and GetActions methods are used for Actions; they are not of interest now. Let us consider the rest of the methods.

  • List<string> GetConditions(string schemeCode) - must return the list of Action-Condition names, used in this implementation of IWorkflowActionProvider. This is the list shown in the Designer. Please, note that schemeCode is passed to these methods, thus, the set of Action-Conditions can differ for each of the schemes.
  • bool IsConditionAsync(string name, string schemeCode) - if the method returns true, the Action-Condition is executed using the asynchronous method ExecuteConditionAsync, otherwise, the synchronous method ExecuteCondition is used.
  • Task<bool> ExecuteConditionAsync(string name, ProcessInstance processInstance, WorkflowRuntime runtime, string actionParameter, CancellationToken token)  - an asynchronous Action-Condition.
  • bool ExecuteCondition(string name, ProcessInstance processInstance, WorkflowRuntime runtime, string actionParameter) - a synchronous * Action*-Condition.

Both of the Action-Condition methods, synchronous and asynchronous, have the following common parameters:

  • name - the name of Action-Condition, specified and used in the process scheme.
  • processInstance - contains the whole information about the process, including the process parameters.
  • runtime - the instance of Workflow Runtime executing the given process.
  • actionParameter - the additional string parameter, set in the scheme designer; can also contain JSON; the setting form of this parameter can be customized.

Condition3

After creating one or more classes with our Action-Conditions, we should connect them to Workflow Runtime on its initialization.

var runtime = new WorkflowRuntime()
...
runtime.WithActionProvider(new YourConditionProvider())
.AsSingleServer(); //.AsMultiServer();

You can connect any number of Action Providers to Workflow Runtime; moreover, in the WithActionProvider method, you can specify the list of the schemes codes to limit your provider with these schemes.

workflowRuntime.WithActionProvider(new YourConditionProvider(), new List<string>() {"SchemeCode1", "SchemeCode2"});

Option Two, Using Code Actions

Code Actions are created in the process scheme, namely, you write the code directly in the Designer. This approach is described in detail here. All you need to do is select the type equal to Condition, check the Async checkbox if your * Action*-Condition is asynchronous, and edit its code. To Action-Conditions created in the scheme, the same parameters are passed as to those created in the provider. That is, processInstance, runtime and actionParameter are available to you.

Condition4