How to integrate

Integrating Workflow Engine into any application takes about an hour and consists of 5 simple steps. This article lists examples for ASP.NET MVC and MS SQL.

Integration with solutions based on other technologies (ASP.NET WebForms, .NET WinForms, ОМЬ) or databases (MySQL, PostgreSQL, Orcale, MongoDB and others) is roughly the same and should noe entail any difficulties.

We recommend you to follow integration steps in this order:

  1. Setting up your database
  2. Initializing WorkflowRuntime
  3. Connecting Designer
  4. Creating a document workflow scheme
  5. Creating a process and calling commands

1. Setting up your database

1.1. Download your persistence provider here.

Provider

1.2. Execute the CreatePersistenceObjects.sql script from the SQL folder in the archive.

CreatePersistenceObjects

CreatePersistenceObjects

After you execute it, the following tables will be created in the database:

Tables

Learn more about persistence.

2. Initializing WorkflowRuntime

2.1. Create a Class Library project in Visual Studio.

2.2. Add the following NuGet packages into your project:

2.3. Add reference System.Configuration.

2.4. Create a WorkflowInit.cs file. Add the following namespaces to the usings section:

using System.Xml.Linq;
using OptimaJet.Workflow.Core.Builder;
using OptimaJet.Workflow.Core.Bus;
using OptimaJet.Workflow.Core.Runtime;
using OptimaJet.Workflow.DbPersistence;

Add the initialization of the WorkflowRuntime object. It should look like this:

public static class WorkflowInit
{
    private static readonly Lazy<WorkflowRuntime> LazyRuntime = new Lazy<WorkflowRuntime>(InitWorkflowRuntime);

    public static WorkflowRuntime Runtime
    {
        get { return LazyRuntime.Value; }
    }

    private static WorkflowRuntime InitWorkflowRuntime()
    {
        //TODO If you have a license key, you have to register it here
        //WorkflowRuntime.RegisterLicense("your license key text");

        //TODO If you are using database different from SQL Server you have to use different persistence provider here.
        var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
        var dbProvider = new MSSQLProvider(connectionString);

        var builder = new WorkflowBuilder<XElement>(
            dbProvider,
            new OptimaJet.Workflow.Core.Parser.XmlWorkflowParser(),
            dbProvider
        ).WithDefaultCache();

        var runtime = new WorkflowRuntime()
            .WithBuilder(builder)
            .WithPersistenceProvider(dbProvider)
            .WithBus(new NullBus())
            //TODO If you have planned use Timers uncomment following line of code
            //.WithTimerManager(new TimerManager())
            .EnableCodeActions()
            .SwitchAutoUpdateSchemeBeforeGetAvailableCommandsOn();
        //events subscription
        runtime.ProcessActivityChanged += (sender, args) => { };
        runtime.ProcessStatusChanged += (sender, args) => { };
        //TODO If you have planned to use Code Actions functionality that required references to external assemblies you have to register them here
        //runtime.RegisterAssemblyForCodeActions(Assembly.GetAssembly(typeof(SomeTypeFromMyAssembly)));

        //starts the WorkflowRuntime
        //TODO If you have planned use Timers the best way to start WorkflowRuntime is somwhere outside of this function in Global.asax for example
        runtime.Start();

        return runtime;
    }
}

If you have a license key, add a call to the WorkflowRuntime.RegisterLicense method with the license key.

You can find more information in documentation.

3. Connecting Designer

3.1. Create an ASP.NET Web Application MVC project or use an existing one. If you are creating a project from scratch, you can use the instructions from Microsoft.

3.2. Add a reference to the project from step 2 to have access to the WorkflowRuntime object.

3.3. Add the following NuGet packages to the project of your solution:

3.4. Add the WorkflowRuntime object call from step 2 to the getRuntime method in the Controllers/DesignController.cs file.

Before:

private WorkflowRuntime getRuntime
{
    get
    {
         //INIT YOUR RUNTIME HERE
         return null;
    }
}

After:

private WorkflowRuntime getRuntime
{
     get
     {
         return WorkflowEngineTest.WorkflowInit.Runtime;
     }
}

3.5. Add a database connection string in the connectionStrings section of the Web.config configuration file:

<connectionStrings>
<add name="ConnectionString"
             connectionString="Data Source=(local);Initial Catalog=WFE;Integrated Security=True;User ID=sa;Password=1"
             providerName="System.Data.SqlClient" />
 </connectionStrings>

Make sure that the address of the server, authentication type,login and password match your settings.

3.6. Probably, you will need to edit the styles of your application and the size of designer in the InitWFE function of the View/Designer/Index.cshtml file.

3.7. Attach the JQuery-UI library. Beware, that first you need to attach jQuery and then and only then jQuery-UI. To do that, include the following lines into the Views/Shared/_Layout.cshtml file:

<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js" type="text/javascript"></script>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/themes/smoothness/jquery-ui.min.css" rel="stylesheet" type="text/css" />

3.8. Run your web project. To do that, right-mouse-button-click on the Views/Designer/Index.cshtml file and click View in Browser.

Run Project

Designer should open.

Designer

If this does not happen, check for errors on the page and refer to FAQ. You can learn more about Designer in documentation.

4. Creating a document workflow scheme

4.1. Create 2 commands: go, back.

Commands

4.2. Create 4 activities: Begin, State1, State2, End. Set the Initial flag for the Begin activity and the Final flag for the End activity.

Activities

4.3. Create transitions between activities. For each transition set the Trigger, Command and Classifier parameters (this step is optional).

Transitions

4.4. Click Save Scheme

You can learn more about Schemes in documentation.

5. Creating a process and calling commands

5.1. Create a Console Application project.

5.2. Add the following NuGet packages to the project:

5.3. Add a Reference to the project from step 2.

5.4. Add a database connection string to the connectionStrings sectino of the App.config configuration file:

<connectionStrings>
<add name="ConnectionString"
             connectionString="Data Source=(local);Initial Catalog=WorkflowEngine;Integrated Security=True;User ID=sa;Password=1"
             providerName="System.Data.SqlClient" />
 </connectionStrings>

Make sure that the address of the server, authentication type, login and password match your settings.

5.5. Study documentation on basic operations.

5.6. Add the following namespaces to the Program.cs file:

using WorkflowEngineTest;
using OptimaJet.Workflow.Core.Runtime;
using System.Threading;

Here's a sample code that shows you how to call basic operations: creating a process, getting a list of available commands, executing commands, setting a state and deleting a process:

class Program
{
    static string schemeCode = "SimpleWF";
    static Guid? processId = null;
    static void Main(string[] args)
    {
        Console.WriteLine("Operation:");
        Console.WriteLine("0 - CreateInstance");
        Console.WriteLine("1 - GetAvailableCommands");
        Console.WriteLine("2 - ExecuteCommand");
        Console.WriteLine("3 - GetAvailableState");
        Console.WriteLine("4 - SetState");
        Console.WriteLine("5 - DeleteProcess");
        Console.WriteLine("9 - Exit");
        Console.WriteLine("The process isn't created.");
        CreateInstance();

        do
        {
            if (processId.HasValue)
            {
                Console.WriteLine("ProcessId = '{0}'. CurrentState: {1}, CurrentActivity: {2}",
                    processId,
                    WorkflowInit.Runtime.GetCurrentStateName(processId.Value),
                    WorkflowInit.Runtime.GetCurrentActivityName(processId.Value));
            }

            Console.Write("Enter code of operation:");
            char operation = Console.ReadLine().FirstOrDefault();

            switch (operation)
            {
                case '0':
                    CreateInstance();
                    break;
                case '1':
                    GetAvailableCommands();
                    break;
                case '2':
                    ExecuteCommand();
                    break;
                case '3':
                    GetAvailableState();
                    break;
                case '4':
                    SetState();
                    break;
                case '5':
                    DeleteProcess();
                    break;
                case '9':
                    return;
                default:
                    Console.WriteLine("Unknown code. Please, repeat.");
                    break;
            }
            Console.WriteLine();
        } while (true);
    }

    private static void CreateInstance()
    {
        processId = Guid.NewGuid();
        try
        {
            WorkflowInit.Runtime.CreateInstance(schemeCode, processId.Value);
            Console.WriteLine("CreateInstance - OK.", processId);
        }
        catch (Exception ex)
        {
            Console.WriteLine("CreateInstance - Exception: {0}", ex.Message);
            processId = null;
        }
    }

    private static void GetAvailableCommands()
    {
        if (processId == null)
        {
            Console.WriteLine("The process isn't created. Please, create process instance.");
            return;
        }
        var commands = WorkflowInit.Runtime.GetAvailableCommands(processId.Value, string.Empty);

        Console.WriteLine("Available commands:");
        if (commands.Count() == 0)
        {
            Console.WriteLine("Not found!");
        }
        else
        {
            foreach (var command in commands)
            {
                Console.WriteLine("- {0} (LocalizedName:{1}, Classifier:{2})", command.CommandName, command.LocalizedName, command.Classifier);
            }
        }
    }

    private static void ExecuteCommand()
    {
        if (processId == null)
        {
            Console.WriteLine("The process isn't created. Please, create process instance.");
            return;
        }
        WorkflowCommand command = null;
        do
        {
            GetAvailableCommands();
            Console.Write("Enter command:");
            var commandName = Console.ReadLine().ToLower().Trim();
            if (commandName == string.Empty)
                return;
            command = WorkflowInit.Runtime.GetAvailableCommands(processId.Value, string.Empty)
                .Where(c => c.CommandName.Trim().ToLower() == commandName).FirstOrDefault();
            if (command == null)
                Console.WriteLine("The command isn't found.");
        } while (command == null);

        WorkflowInit.Runtime.ExecuteCommand(command, string.Empty, string.Empty);
        Console.WriteLine("ExecuteCommand - OK.", processId);
    }

    private static void GetAvailableState()
    {
        if (processId == null)
        {
            Console.WriteLine("The process isn't created. Please, create process instance.");
            return;
        }
        var states = WorkflowInit.Runtime.GetAvailableStateToSet(processId.Value, Thread.CurrentThread.CurrentCulture);
        Console.WriteLine("Available state to set:");
        if (states.Count() == 0)
        {
            Console.WriteLine("Not found!");
        }
        else
        {
            foreach (var state in states)
            {
                Console.WriteLine("- {0}", state.Name);
            }
        }
    }

    private static void SetState()
    {
        if (processId == null)
        {
            Console.WriteLine("The process isn't created. Please, create process instance.");
            return;
        }
        string stateName = string.Empty;
        WorkflowState state;
        do
        {
            GetAvailableState();
            Console.Write("Enter state:");
            stateName = Console.ReadLine().ToLower().Trim();
            if (stateName == string.Empty)
                return;
            state = WorkflowInit.Runtime.GetAvailableStateToSet(processId.Value, Thread.CurrentThread.CurrentCulture)
                .Where(c => c.Name.Trim().ToLower() == stateName).FirstOrDefault();
            if (state == null)
                Console.WriteLine("The state isn't found.");
            else
                break;
        } while (true);
        if (state != null)
        {
            WorkflowInit.Runtime.SetState(processId.Value, string.Empty, string.Empty, state.Name, new Dictionary<string, object>());
            Console.WriteLine("SetState - OK.", processId);
        }
    }

    private static void DeleteProcess()
    {
        if (processId == null)
        {
            Console.WriteLine("The process isn't created. Please, create process instance.");
            return;
        }
        WorkflowInit.Runtime.PersistenceProvider.DeleteProcess(processId.Value);
        Console.WriteLine("DeleteProcess - OK.", processId);
        processId = null;
    }
}

5.7. Run your project. To do that, right-mouse-button-click the project and click Start new instance.

Run Project

The console application where you can create processes and execute commands should launch.

Console Application

You can learn more about basic operations here.

Conclusion

If you completed all the aforementioned steps, by now you should have a project that looks like this.

Workflow Engine is one of the easiest workflow engines for document approval when it comes to integration. We recommend it to companies who develop information systems with workflow functionality.

You can download our samples here.

If you have any questions, please, do not hesitate to contact us.

Top