Using services

Services invocation is the most important part of real workflows. Without it, workflows would be useless because in most of the cases you start a workflow for the side effects caused by its service calls.

In Flower terms, a service is not necessarily a WCF or a web service; it is literally any object. Normally, these objects are service proxies, but if you wish to execute a code right in the application domain of the processor, you can use the implementations as a service directly. This may be useful for lightweight services or helpers.

Registering services

Flower client API instantiates services by user requests from Spring.NET IoC containers whose XML configurations are stored in the directory under '/Services'. A service is an object defined in a IoC container.

To obtain a service instance, applications and processors use IFlowerClient.GetService<> method. This method retrieves a service by its path and returns its instance. A path of a service consists of the path of its container and the id of the service in the container. For example, if you have a service container '/Services/MyContainer' and an object definition in it:

<object id="MyService" type="...">
    ...
</object>


Then the path of the defined service will be '/Services/MyContainer/MyService'.

A service container can be registered in the directory by using {createOrUpdateServiceContainer} utility function. First, create an XML IoC container configuration. Then, in the management console run the following:

createOrUpdateServiceContainer({
    name: 'Your/Service/Container/Path', // The path of the service container relative to '/Services'.
    from: 'YourServicesContainer.xml'    // The file system path to the XML containing IoC container configuration.
});


It's usually required to have object definitions that are available in all service containers. An example of such objects is the binding configurations. You may wish to have a set of binding configurations which multiple services could share. For such purposes there is a special folder '/Services/Flower'. Objects defined in the service containers in this folder are implicitly available in all service containers, but not directly accessible via the client API.

Services invocation

A service can be invoked from a workflow via Invoke statement. There are two versions of Invoke: the one accepting an instance of an injected service, and the one obtaining a service instance by path.

To obtain a service instance via injection, define a field of the service type (interface) in the workflow class and mark it with Service attribute. Then, in DefineProcess, call Invoke and pass the field as the first argument.

[Service("MyContainer/MyService")]
public IMyService MyService;

public void DefineProcess(IProcessBuilder process)
{
    process
        ...
        .Invoke(MyService, (_, svc) => svc.MyMethod())
        ...
}

Technically, you can invoke an injected service from Exec or ExecAndSave, but it's not a good idea because Flower will not know that you're invoking a service. Telling Flower about that may be required for benchmark tools and troubleshooting. If you need to call multiple services in a single transaction, consider placing this transaction in a single method of a lightweight service (helper); then, call this method via Invoke.

Also, you can define a field or property of type Flower.Services.IDirectory to get access to the directory service instance. This may be useful if you need to implement a maintenance workflow.

You may have multiple services with the same interface registered in the directory (they, for example, may work each with different regional database). In this case, you may wish to select a service dynamically in runtime deriving its path from the data of your workflow. You can do that by using Invoke accepting a lambda calculating a service path.

public void DefineProcess(IProcessBuilder process)
{
    process
        ...
        .Invoke<IMyService>
        (
            _ => "MyService/" + request.RegionCode,
            (_, svc) => svc.MyMethod()
        )
        ...
}

In both versions of Invoke you can override the default execution behaviour of the activity by specifying the execution flags as the last (optional) parameter:

public void DefineProcess(IProcessBuilder process)
{
    process
        ...
        .Invoke<IMyService>
        (
            _ => "MyService/" + request.RegionCode,
            (_, svc) => svc.MyMethod(),
            // To disable transaction scope.
            ExecutionFlags.NoTransaction |
            // To skip the process state saving.
            ExecutionFlags.DontSave |
            // To prevent retries in case of failure.
            ExecutionFlags.DontRetry |
        )
        ...
}

Services can be invoked in the Initialize method of a workflow class. The injected service are available in Initialize. Also, the services can be obtained by the call:

context.ServicesProvider.GetService<IMyService>("/Services/MyServicesContainer/MyService")

Last edited Jan 30, 2014 at 6:34 PM by dbratus, version 5

Comments

No comments yet.