Process Parameters
General information
Workflow Engine allows you to store any parameter together with the process, whether a simple value (string, number, bool) or a complex multi-level object. You can store data of any type together with the process, the only limitation for this type is to be serializable in JSON and deserializable backwards. The process parameters are classified as follows:
- Persistent and Temporary parameters. Persistent parameter is stored in the database, while Temporary one exists only during the process execution.
- Explicit and Implicit parameters. An Explicit parameter is described explicitly in the process scheme; that is, its type, name and default value are specified directly in the scheme. An Implicit parameter is created by the code called by the process.
- Dynamic parameters. You are not constrained to set a specific type for the parameter (for
example,
YourNamespace.YourBusinessObject
); instead, the special typeOptimaJet.Workflow.Core.Model.DynamicParameter
can be used. Any object of this type can be stored as a process parameter, and subsequently you should not think about its type when saving or calling. This is a special dynamic type of .NET. - Full and Partial parameters. That is simple: the process parameters are always identified by name, for example, 'ParameterName'. But if you have a complex multi-level object stored as a parameter, then you are free to work with its part as an independent parameter. Use the name 'ParameterName.PropertyName' to do that.
- External parameters. These are passed to the process through a special provider, while their handling do not differ from work with a usual parameter. For example, it is useful if you constantly call a database record in your code but don’t want to add this record to the process parameters.
The types of parameters given above are not mutually exclusive, for example, one of the most commonly used types of parameters is a Persistent Implicit Dynamic parameter.
Let us consider the details of work with parameters and their use cases.
Application of Parameters
Before proceeding to a detailed description of parameters, we give a brief overview of their possible application.
- In Actions code.
- In Conditions and Expressions code.
- In Authorization Rules code.
- In Action Parameters substitutions.
Thus, parameters are one of the most valuable and frequently used features in Workflow Engine.
Persistent and Temporary Parameters
If you are going to save the process parameters while the very process is the idle state, use Persistent parameters. If you pass a parameter to the process (for example, when creating it, executing a command, or setting a state), and this parameter value is used only at the stage of the process execution caused by your action, then use Temporary parameters. Thus, you can control the availability of parameters at various stages of the process.
Parameter accessibility area means a moment in time when it is possible to get the parameter value (by calling
the GetParameter
method). You can pass parameters to the process at any time. Parameter accessibility area depends on
the parameter is Persistent or Temporary.
- Temporary parameter will be accessible from the moment of its passing (through a command, initial process
parameters, or by calling the
SetParameter
method) until the end of a transition process, i.e. until the time when the process stops and starts waiting for the next command. - Persistent parameter will be accessible at any time.
Parameter Identification and Naming
All parameters are identified by a unique string name; this name can be any, but the dot character '.' is not recommended to use. Now we explain, why. If a simple type is used as a parameter value (a string, for example), then everything is plain; suppose, we have named the parameter 'StringParameter', then, we will get its value using the name 'StringParameter'. But for a multi-level object, such as:
var ObjectParameter = new
{
ObjectProperty = new
{
StringProperty = "Some string"
},
IntProperty = 42
};
We can work with the whole of such a parameter, using its name 'ObjectParameter', or with its separate parts using the mechanism of Partial parameters, for example:
- 'ObjectParameter.ObjectProperty' - the object property of the parameter.
- 'ObjectParameter.ObjectProperty.StringProperty' - the string property of the parameter.
- 'ObjectParameter.IntProperty' - the integer property of the parameter.
Explicit and Implicit Parameters
In most cases, Implicit parameters are used. They are not described in the scheme and appear in the process immediately after you have passed them. When identifying a parameter, you should specify, if it is Persistent or Temporary. By default, parameters are considered Temporary. For example, the following code creates a Persistent process parameter:
processInstance.SetParameter("ObjectParameter", objectParameter, ParameterPurpose.Persistence);
In the next section, we explain how to describe Explicit parameters in the process scheme.
Passing Parameters to Process
There are four ways to pass parameters to the process.
Passing parameters when creating a process:
var createInstanceParameters = new CreateInstanceParams("SchemeCode", processId)
.AddPersistentParameter("StringParameter", "Some String")
.AddPersistentParameter("ObjectParameter", ObjectParameter)
.AddTemporaryParameter("TemporaryString", "Some Temporary String");
runtime.CreateInstance(createInstanceParameters);
Please, note that we specify whether the parameter is Temporary or Persistent. Partial parameters can be also used, for example, in the code below, the object 'ObjectParameter' is created part by part:
var createInstanceParameters = new CreateInstanceParams("SchemeCode", processId)
.AddPersistentParameter("ObjectParameter.ObjectProperty.StringProperty", "Some String")
.AddPersistentParameter("ObjectParameter.IntProperty", 42);
runtime.CreateInstance(createInstanceParameters);
Passing parameters when executing a command:
var command = runtime.GetAvailableCommands(...).FirstOrDefault();
// Persisted parameter
command.SetParameter("StringParameter", "Some string", persist:true);
// Persisted or temporary parameter
command.SetParameter("TemporaryString", "Some Temporary String");
Please, pay attention to the following: the parameter 'StringParameter' is always Persistent. The 'TemporaryString' parameter is a bit trickier.
- if a parameter with this name has already been passed to the process before as a Persistent parameter, then it remains Persistent;
- if a parameter with this name is declared in the scheme as Persistent (see below), then it remains Persistent;
- in all other cases, the parameter is Temporary.
You can also use Partial parameters.
var command = runtime.GetAvailableCommands(...).FirstOrDefault();
// Persisted parameter
command.SetParameter("ObjectParameter.ObjectProperty.StringProperty", "Some String", persist:true);
Passing parameters when setting a state:
var setStateParameters = new SetStateParams(processId, "StateName")
.AddPersistentParameter("StringParameter", "Some String")
.AddPersistentParameter("ObjectParameter", ObjectParameter)
.AddTemporaryParameter("TemporaryString", "Some Temporary String");
runtime.SetState(setStateParameters);
In this case, the parameter-passing principles do not differ from the two cases described above.
Passing parameters without changing the process state, use the following code:
workflowRuntime.SetPersistentProcessParameter(processId, "StringParameter", "SomeString");
workflowRuntime.SetPersistentProcessParameter(processId, "ObjectParameter.ObjectProperty.StringProperty", "SomeString");
Let us make a short summary. There are several ways to pass a parameter to the process. If you have explicitly specified the parameter as Persistent, it remains Persistent. If the parameter is specified as Temporary, but previously it was Persistent, the parameter remains Persistent.
Explicit Parameters
Explicit parameters are described in the scheme. Parameters of this type are rarely used. They can be handy for auto-generating forms in accordance with the process scheme. When Parameter is created, the following properties are specified:
-
Name - Parameter name. All Parameters are identified by their name. Name is case-sensitive;
-
Type - a CLR type of Parameter. The list has an autocomplete feature that suggests available names. Primitive types may be specified with a short name without a namespace:
String
,Int32
,Guid
, etc. Other types should be specified with the full namespace:Business.SomeEntity
,System.Text.Encoding
, etc. Generic types are specified in the following way:System.Collections.Generic.Dictionary<String, Int32>
,System.Collections.Generic.List<Business.SomeEntity>
. By default, the autocomplete suggests only primitive types. The registration of types for the autocomplete is performed in Designer with theruntime.RegisterAssemblyForCodeActions(...)
method. By default, all types from an assembly will be displayed in Designer. To avoid this, a filter function may be set. For example, the following code will register only types from theSystem.Collections.Generic
namespace:runtime.RegisterAssemblyForCodeActions(
Assembly.GetAssembly(typeof(System.Collections.Generic.List<>)),
false,
type => type.FullName.StartsWith("System.Collections.Generic")
); -
Purpose - Parameters may be Temporary or Persistent.
-
Initial Value - is the initial value of Parameter. It can be specified only for Persistent parameters. It contains the initial value of Parameter, which will be assigned to the latter immediately after the process has been created. Value represents JSON. Open the JSON editor to see what it looks like. For example, the Format button formats the JSON; the Create button creates an object's sample in the following way: first, it creates an object with the requested type on the server, which is further serialized in JSON and displayed in Designer. It is quite convenient when working with large business objects.
Using Parameters in Process Execution. Dynamic Parameters
From the code of your Actions, Conditions and Rules, you can get process parameter values. Moreover, inside Actions, you
can set the process parameter values. Actions, Conditions, and Rules are methods; one of the input parameters of them
is processInstance
. Using the process instance, you can get and set the parameters.
To get a parameter, proceed as follows:
string stringParameter = processInstance.GetParameter<string>("StringParameter");
SomeCustomType objectParameter = processInstance.GetParameter<SomeCustomType>("ObjectParameter");
In this code, we know the parameter type, we use it and get the value of this type. But, what to do if the type is
unknown. Below, we explain how to pass objects of anonymous types or use partial parameters to get the process
parameters without a strict type. In such a case, the special dynamic
type OptimaJet.Workflow.Core.Model.DynamicParameter
is used.
dynamic objectParameter = processInstance.GetParameter<DynamicParameter>("ObjectParameter");
string stringProperty = objectParameter.ObjectProperty.StringProperty;
int intProperty = objectParameter.IntProperty;
dynamic stringParameter = processInstance.GetParameter<DynamicParameter>("StringParameter");
bool isString = stringParameter is string; // it will be true
As shown in this code, the actual type of the parameter can be any, and OptimaJet.Workflow.Core.Model.DynamicParameter
figures out what it is. You can get the object and work with it as usual. In some ways, this approach resembles working
with javascript objects.
The GetParameter
method also has an overloaded asynchronous version, but it is advisable to use it with
External parameters only. See in detail below.
To set a parameter, proceed as follows:
processInstance.SetParameter("ParameterName", parameterValue);
In this case, if the parameter was declared in the scheme as Persistent, or was previously passed to the process as * Persistent*, then this parameter remains Persistent.
processInstance.SetParameter("ParameterName", parameterValue, ParameterPurpose.Persistence);
In this case, the parameter always remains Persistent. Thus, if you wish to create a Persistent Implicit parameter, use this code.
Also, a parameter can be set part by part; as we wrote above, a Partial parameter is used to do that.
processInstance.SetParameter("ObjectParameter.ObjectProperty.StringProperty", "SomeNewString");
To set your parameters without fail, always use the method processInstance.SetParameter (...)
, whether it
is a value or a reference type.
Deleting Parameters and Checking for Existence
The following methods allows you to remove or check the existence of the parameter. They also work with Partial parameters.
Removing Persistence's Parameter. Removal of Persistence's Parameter will also take place, if Parameter's Value is set to null. Parameters that are null are removed from the database:
processInstance.RemoveParameter("ParameterName");
Checking whether Parameter exists in the process:
bool isExisting = processInstance.IsParameterExisting("ParameterName");
Passing Dynamic Parameters to Process
Let us consider a particular case. We want to store a complex object in the process parameters, work with it, but we do not want to create a new type (class or structure) for storing this object. To pass this object, we can use an anonymous type.
var ObjectParameter = new
{
ObjectProperty = new
{
StringProperty = "Some string"
},
IntProperty = 42
};
To pass the value from outside of the process:
var createInstanceParameters = new CreateInstanceParams("SchemeCode", processId)
.AddPersistentParameter("ObjectParameter", ObjectParameter);
runtime.CreateInstance(createInstanceParameters);
Or, to set the value inside the process:
processInstance.SetParameter("ObjectParameter", ObjectParameter, ParameterPurpose.Persistence);
The second option is to use partial parameters. To pass the value from outside of the process:
var createInstanceParameters = new CreateInstanceParams("SchemeCode", processId)
.AddPersistentParameter("ObjectParameter.ObjectProperty.StringProperty", "Some String")
.AddPersistentParameter("ObjectParameter.IntProperty", 42);
runtime.CreateInstance(createInstanceParameters);
Or, to set the value inside the process:
processInstance.SetParameter("ObjectParameter.ObjectProperty.StringProperty", "Some String", ParameterPurpose.Persistence);
processInstance.SetParameter("ObjectParameter.IntProperty", 42, ParameterPurpose.Persistence);
Then, we can use a dynamic parameter to work with this object:
dynamic objectParameter = processInstance.GetParameter<DynamicParameter>("ObjectParameter");
// 42 + 1 = 43
objectParameter.IntProperty = objectParameter.IntProperty + 1;
// "Some String" -> "NEW Some String"
objectParameter.ObjectProperty.StringProperty = $"NEW {objectParameter.ObjectProperty.StringProperty}";
processInstance.SetParameter("ObjectParameter", objectParameter);
External Parameters
External is a special kind of parameters; they are not stored in the process, but in your external (relative to the
Workflow Engine) data store. For example, you want to handle a document record as a regular parameter, but do not want
to pass it to the process. The integration interface IWorkflowExternalParametersProvider
is used for that. To use
external parameters in your workflow, proceed as follows:
Write a class implementing IWorkflowExternalParametersProvider
:
public interface IWorkflowExternalParametersProvider
{
Task<object> GetExternalParameterAsync(string parameterName, ProcessInstance processInstance);
object GetExternalParameter(string parameterName, ProcessInstance processInstance);
Task SetExternalParameterAsync(string parameterName, object parameterValue, ProcessInstance processInstance);
void SetExternalParameter(string parameterName, object parameterValue, ProcessInstance processInstance);
bool IsGetExternalParameterAsync(string parameterName, string schemeCode);
bool IsSetExternalParameterAsync(string parameterName, string schemeCode);
bool HasExternalParameter(string parameterName, string schemeCode);
}
Its methods serve the following purposes:
HasExternalParameter
- the method takes the parameter name. If you use a partial parameter, for example,ObjectParameter.ObjectProperty.StringProperty
, then the name -ObjectParameter
- is taken, but not its part. If the provider supports this parameter, then it returnstrue
.IsGetExternalParameterAsync
- if the parameter getter is asynchronous, returnstrue
; if it is synchronous, returnsfalse
.GetExternalParameterAsync
- ifIsGetExternalParameterAsync
returnstrue
, the asynchronous parameter getter is called. It should return the parameter value.GetExternalParameter
- ifIsGetExternalParameterAsync
returnsfalse
, the synchronous parameter getter is called. It should return the parameter value.IsSetExternalParameterAsync
- should returntrue
if the parameter setter is asynchronous, andfalse
- otherwise.SetExternalParameterAsync
- calls the asynchronous parameter setter, ifIsSetExternalParameterAsync
returnstrue
. It takes the set value of the external parameter, changed usingprocessInstance.SetParameter(...)
orprocessInstance.SetParameterAsync(...)
.SetExternalParameter
- calls the synchronous parameter setter, ifIsSetExternalParameterAsync
returnsfalse
. It should return the parameter value. It takes the set value of the external parameter, changed usingprocessInstance.SetParameter(...)
orprocessInstance.SetParameterAsync(...)
.
After implementing (once, or more) the External Parameters Provider, you should connect it to the Workflow Runtime when configuring it.
var runtime = new WorkflowRuntime()
...
.WithExternalParametersProvider(new YourCustomExternalParametersProvider())
.AsSingleServer(); //.AsMultiServer();
It is possible to register several External Parameters Providers in one runtime. Moreover, you can optionally pass a
list of schemes to the WithExternalParametersProvider
method, to limit the schemes where the given provider is used.
At last, we consider how the GetExternalParameter
and SetExternalParameter
methods work with the object
type. That
is, methods GetExternalParameter
and SetExternalParameter
take the value of the parameter as an object
and return
the object
. This object
can be one of the following types:
-
a
DynamicParameter
object - in this case, the properties of your external parameter are changed as fast as possible, since no additional transformations are carried out. However, you should convert your data toDynamicParameter
and backwards by hands. ADynamicParameter
object is also passed to theSetExternalParameter
method. -
an object implementing interface
IDynamicParameterCompatible
. This interface is quite simple and contains the two methods only:public interface IDynamicParameterCompatible
{
IDictionary<string, object> GetPropertiesAsDictionary();
void SetPropertiesFromDictionary(IDictionary<string, object> properties);
}GetPropertiesAsDictionary
- returns all the properties of an object as a dictionary.SetPropertiesFromDictionary
- sets all the properties of an object from a dictionary.
This method is fast, and the only thing you are to do is to convert your object into a dictionary. Please, note that if you want to use
processInstance.SetParameter(...)
with a Partial parameter setting, then your type must have a public parameterless constructor. An object of the type returned from theGetExternalParameter
method is passed to theSetExternalParameter
method. -
A dictionary
IDictionary<string, object>
. In this case, the same dictionary is passed to theSetExternalParameter
method. -
An object of any other type. If you only plan to read the whole external parameter or change the whole parameter (that is, do not call
processInstance.SetParameter(...)
with a Partial parameter setting, and do not call theprocessInstance.GetParameter<T>(...)
method with the path to the parameter parts), the type can be any. However, if you use Partial External parameters, the type must be serialized in JSON and deserialized backwards.
When you get an external parameter using the processInstance.GetParameter<T>(...)
method, it is cached; subsequent
calls of GetParameter
for the same object take values from the cache, and the GetExternalParameter
method is not
used. However, any call to processInstance.SetParameter(...)
for the same external parameter will clear this cache.
Also, you can not call processInstance.GetParameter<T>(...)
before changing the external
parameter. processInstance.SetParameter(...)
will load the external parameter, change it and send it
to SetExternalParameter
on its own.
System Parameters
You may access System parameters trough special properties of the processInstance
object. Here are the main ones:
processInstance.ProcessId
- Guid - process identifier;processInstance.SchemeId
- Guid - scheme identifier from the WorkflowProcessScheme table;processInstance.SchemeCode
- string - scheme code (name);processInstance.RootProcessId
- Guid - root process identifier; equals toProcessId
for the base root process;processInstance.ParentProcessId
- Guid - parent process identifier;processInstance.CurrentState
- current recorded state name;processInstance.PreviousState
- previous recorded state name;processInstance.PreviousStateForDirect
- name of the previous state recorded at the direct Classifier = Direct transition;processInstance.PreviousStateForReverse
- name of the previous state recorded at the reversal Classifier = Reverse transition;processInstance.ExecutedActivityState
- executed Activity's state name;processInstance.CurrentActivity
- name of the current recorded Activity;processInstance.PreviousActivity
- name of the previous recorded Activity;processInstance.PreviousActivityForDirect
- name of the previous Activity recorded at the direct Classifier = Direct transition;processInstance.PreviousActivityForReverse
- name of the previous Activity recorded at the reversal Classifier = Reverse transition;processInstance.ExecutedActivity
-ActivityDefinition
whose Actions are currently being implemented;processInstance.ExecutedTransition
-TransitionDefinition
which is currently being executed;processInstance.StartTransitionalProcessActivity
- name of Activity that started the transition process;processInstance.CurrentCommand
- name of the currently executed Command;processInstance.ExecutedTimer
- name of the currently executed Timer;processInstance.IdentityId
- user identifier conveyed troughExecuteCommand
orSetState
;processInstance.ImpersonatedIdentityId
- impersonated user identifier conveyed troughExecuteCommand
orSetState
;processInstance.IsPreExecution
- returns true, if Action was called in the Pre-execution mode;processInstance.IdentityIds
- list of identifiers of users who potentially could execute a transition to Activity. Specified only in the Pre-execution mode;processInstance.IdentityIdsForCurrentActivity
- list of identifiers of users who can execute a transition from the executed Activity. Specified only in the Pre-execution mode.