Skip to main content

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

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 type OptimaJet.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.

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 the runtime.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 the System.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.

caution

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");
caution

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 returns true.
  • IsGetExternalParameterAsync - if the parameter getter is asynchronous, returns true; if it is synchronous, returns false.
  • GetExternalParameterAsync - if IsGetExternalParameterAsync returns true, the asynchronous parameter getter is called. It should return the parameter value.
  • GetExternalParameter - if IsGetExternalParameterAsync returns false, the synchronous parameter getter is called. It should return the parameter value.
  • IsSetExternalParameterAsync - should return true if the parameter setter is asynchronous, and false - otherwise.
  • SetExternalParameterAsync - calls the asynchronous parameter setter, if IsSetExternalParameterAsync returns true. It takes the set value of the external parameter, changed using processInstance.SetParameter(...) or processInstance.SetParameterAsync(...).
  • SetExternalParameter - calls the synchronous parameter setter, if IsSetExternalParameterAsync returns false. It should return the parameter value. It takes the set value of the external parameter, changed using processInstance.SetParameter(...) or processInstance.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 to DynamicParameter and backwards by hands. A DynamicParameter object is also passed to the SetExternalParameter 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 the GetExternalParameter method is passed to the SetExternalParameter method.

  • A dictionary IDictionary<string, object>. In this case, the same dictionary is passed to the SetExternalParameter 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 the processInstance.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 to ProcessId 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 trough ExecuteCommand or SetState;
  • processInstance.ImpersonatedIdentityId - impersonated user identifier conveyed trough ExecuteCommand or SetState;
  • 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.