All Versions
Latest Version
Avg Release Cycle
34 days
Latest Release
24 days ago

Changelog History
Page 1

  • v3.1.0

    January 05, 2020

    Workflow Core 3.1

    Decision Branching

    You can define multiple independent branches within your workflow and select one based on an expression value.

    🏗 For the fluent API, we define our branches with the CreateBranch() method on the workflow builder. We can then select a branch using the Decision step.

    This workflow will select branch1 if the value of data.Value1 is one, and branch2 if it is two.

    var branch1 = builder.CreateBranch() .StartWith\<PrintMessage\>() .Input(step =\> step.Message, data =\> "hi from 1") .Then\<PrintMessage\>() .Input(step =\> step.Message, data =\> "bye from 1");var branch2 = builder.CreateBranch() .StartWith\<PrintMessage\>() .Input(step =\> step.Message, data =\> "hi from 2") .Then\<PrintMessage\>() .Input(step =\> step.Message, data =\> "bye from 2");builder .StartWith\<HelloWorld\>() .Decide(data =\> data.Value1) .Branch("one", branch1) .Branch("two", branch2);

    The JSON representation would look something like this.

    { "Id": "DecisionWorkflow", "Version": 1, "DataType": "MyApp.MyData, MyApp", "Steps": [{ "Id": "decide", "StepType": "WorkflowCore.Primitives.Decide, WorkflowCore", "Inputs": { "Expression": "data.Value1" }, "OutcomeSteps": { "Print1": "\"one\"", "Print2": "\"two\"" } }, { "Id": "Print1", "StepType": "MyApp.PrintMessage, MyApp", "Inputs": { "Message": "\"Hello from 1\"" } }, { "Id": "Print2", "StepType": "MyApp.PrintMessage, MyApp", "Inputs": { "Message": "\"Hello from 2\"" } }] }

    Outcomes for JSON workflows

    You can now specify OutcomeSteps for a step in JSON and YAML workflow definitions.

        "<<Step1 Id>>": "<<expression>>",
        "<<Step2 Id>>": "<<expression>>"

    ⏱ If the outcome of a step matches a particular expression, that step would be scheduled as the next step to execute.

  • v3.0.0

    December 22, 2019

    Workflow Core 3.0.0

    👌 Support for PostgeSQL is delayed because of this issue with upstream libraries

    📦 Split DSL into own package

    📦 The JSON and YAML definition features into their own package.

    Migration required for existing projects:

    • 📦 Install the WorkflowCore.DSL package from nuget.
    • Call AddWorkflowDSL() on your service collection.


    An activity is defined as an item on an external queue of work, that a workflow can wait for.

    In this example the workflow will wait for activity-1, before proceeding. It also passes the value of data.Value1 to the activity, it then maps the result of the activity to data.Value2.

    👷 Then we create a worker to process the queue of activity items. It uses the GetPendingActivity method to get an activity and the data that a workflow is waiting for.

    public class ActivityWorkflow : IWorkflow\<MyData\> { public void Build(IWorkflowBuilder\<MyData\> builder) { builder .StartWith\<HelloWorld\>() .Activity("activity-1", (data) =\> data.Value1) .Output(data =\> data.Value2, step =\> step.Result) .Then\<PrintMessage\>() .Input(step =\> step.Message, data =\> data.Value2); } } ...var activity = host.GetPendingActivity("activity-1", "worker1", TimeSpan.FromMinutes(1)).Result;if (activity != null) { Console.WriteLine(activity.Parameters); host.SubmitActivitySuccess(activity.Token, "Some response data"); }

    The JSON representation of this step would look like this

    { "Id": "activity-step", "StepType": "WorkflowCore.Primitives.Activity, WorkflowCore", "Inputs": { "ActivityName": "\"activity-1\"", "Parameters": "data.Value1" }, "Outputs": { "Value2": "step.Result" } }
  • v2.1.2

    October 06, 2019

    Workflow Core 2.1.2

    • ➕ Adds a feature to purge old workflows from the persistence store.

    🆕 New IWorkflowPurger service that can be injected from the IoC container

    Task PurgeWorkflows(WorkflowStatus status, DateTime olderThan)

    Implementations are currently only for SQL Server, Postgres and MongoDB

  • v2.1.0

    September 15, 2019

    Workflow Core 2.1.0

    • ➕ Adds the SyncWorkflowRunner service that enables workflows to be executed synchronously, you can also avoid persisting the state to the persistence store entirely


    var runner = serviceProvider.GetService\<ISyncWorkflowRunner\>(); ...var worfklow = await runner.RunWorkflowSync("my-workflow", 1, data, TimeSpan.FromSeconds(10));
  • v2.0.0

    June 30, 2019

    Workflow Core 2.0.0

    ⬆️ Upgrade notes

    Existing JSON definitions will be loaded as follows

    using WorkflowCore.Services.DefinitionStorage; ...DefinitionLoader.LoadDefinition(json, Deserializers.Json);

    Targets .NET Standard 2.0

    The core library now targets .NET Standard 2.0, in order to leverage newer features.

    👌 Support for YAML definitions

    ➕ Added support for YAML workflow definitions, which can be loaded as follows

    using WorkflowCore.Services.DefinitionStorage; ...DefinitionLoader.LoadDefinition(json, Deserializers.Yaml);

    Existing JSON definitions will be loaded as follows

    using WorkflowCore.Services.DefinitionStorage; ...DefinitionLoader.LoadDefinition(json, Deserializers.Json);

    Object graphs and inline expressions on input properties

    You can now pass object graphs to step inputs as opposed to just scalar values

      "Body": {
          "Value1": 1,
          "Value2": 2
      "Headers": {
          "Content-Type": "application/json"

    If you want to evaluate an expression for a given property of your object, simply prepend and @ and pass an expression string

      "Body": {
          "@Value1": "data.MyValue * 2",
          "Value2": 5
      "Headers": {
          "Content-Type": "application/json"

    👌 Support for enum values on input properties

    If your step has an enum property, you can now just pass the string representation of the enum value and it will be automatically converted.

    Environment variables available in input expressions

    You can now access environment variables from within input expressions.

  • v1.9.3

    April 07, 2019

    Workflow Core 1.9.3

    • 🛠 Fixes the order of processing for multiple events with same name/key
    • ➕ Adds UseMaxConcurrentWorkflows to WorkflowOptions to allow overriding the max number of concurrent workflows for a given node
  • v1.9.2

    March 31, 2019

    Workflow Core 1.9.2

    🔄 Changes the default retry behavior for steps within a saga to bubble up to the saga container.
    This means you do not have to explicitly set each step within the saga to Compensate.

  • v1.8.3

    February 24, 2019
    • ➕ Added Attach and Id to fluent API
      This will enable one to attach the flow from a step to any other step with an Id
      Control structure scope will be preserved

      .StartWith<Step1>() .Id("step1") .Then<Step2>() .Attach("step1")

    • ➕ Added index queue ahead of upcoming feature for async indexing

    • 🐎 Various performance improvements

  • v1.8.1

    February 03, 2019

    Workflow Core 1.8.1

    Thank you to @MarioAndron

    🚀 This release adds a feature where a DI scope is created around the construction of steps that are registered with your IoC container.

    This enables steps to consume services registered as scoped.

  • v1.8

    January 19, 2019

    Workflow Core 1.8

    🔌 Elasticsearch plugin for Workflow Core

    🔌 A search index plugin for Workflow Core backed by Elasticsearch, enabling you to index your workflows and search against the data and state of them.

    🔧 Configuration

    🏗 Use the .UseElasticsearch extension method on IServiceCollection when building your service provider

    using Nest; =\>{ ... cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://localhost:9200")), "index\_name"); });


    Inject the ISearchIndex service into your code and use the Search method.

    Search(string terms, int skip, int take, params SearchFilter[] filters)


    A whitespace separated string of search terms, an empty string will match everything.
    0️⃣ This will do a full text search on the following default fields

    • Reference
    • Description
    • Status
    • Workflow Definition

    In addition you can search data within your own custom data object if it implements ISearchable

    using WorkflowCore.Interfaces; ...public class MyData : ISearchable{ public string StrValue1 { get; set; } public string StrValue2 { get; set; } public IEnumerable\<string\> GetSearchTokens() { return new List\<string\>() { StrValue1, StrValue2 }; } }

    Search all fields for "puppies"

    searchIndex.Search("puppies", 0, 10);

    skip & take

    👉 Use skip and take to page your search results. Where skip is the result number to start from and take is the page size.


    You can also supply a list of filters to apply to the search, these can be applied to both the standard fields as well as any field within your custom data objects.
    There is no need to implement ISearchable on your data object in order to use filters against it.

    The following filter types are available

    • ScalarFilter
    • DateRangeFilter
    • NumericRangeFilter
    • StatusFilter

    These exist in the WorkflowCore.Models.Search namespace.


    Filtering by reference

    using WorkflowCore.Models.Search; ...searchIndex.Search("", 0, 10, ScalarFilter.Equals(x =\> x.Reference, "My Reference"));

    Filtering by workflows started after a date

    searchIndex.Search("", 0, 10, DateRangeFilter.After(x =\> x.CreateTime, startDate));

    Filtering by workflows completed within a period

    searchIndex.Search("", 0, 10, DateRangeFilter.Between(x =\> x.CompleteTime, startDate, endDate));

    Filtering by workflows in a state

    searchIndex.Search("", 0, 10, StatusFilter.Equals(WorkflowStatus.Complete));

    Filtering against your own custom data class

    class MyData{ public string Value1 { get; set; } public int Value2 { get; set; } }searchIndex.Search("", 0, 10, ScalarFilter.Equals\<MyData\>(x =\> x.Value1, "blue moon"));searchIndex.Search("", 0, 10, NumericRangeFilter.LessThan\<MyData\>(x =\> x.Value2, 5))

    Action Inputs / Outputs

    ➕ Added the action Input & Output overloads on the fluent step builder.

    Input(Action\<TStepBody, TData\> action);

    This will allow one to manipulate properties on the step before it executes and properties on the data object after it executes, for example

    Input((step, data) =\> step.Value1 = data.Value1)
    .Output((step, data) =\> data["Value3"] = step.Output)
    .Output((step, data) =\> data.MyCollection.Add(step.Output))

    💥 Breaking changes

    The existing ability to assign values to entries in dictionaries or dynamic objects on .Output was problematic,
    since it broke the ability to pass collections on the Output mappings.

    .Output(data =\> data["Value3"], step =\> step.Output)

    🚚 This feature has been removed, and it is advised to use the action Output API instead, for example

    .Output((step, data) =\> data["Value3"] = step.Output)

    This functionality remains intact for JSON defined workflows.