TypedWorkflow 1.4.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package TypedWorkflow --version 1.4.0                
NuGet\Install-Package TypedWorkflow -Version 1.4.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TypedWorkflow" Version="1.4.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add TypedWorkflow --version 1.4.0                
#r "nuget: TypedWorkflow, 1.4.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install TypedWorkflow as a Cake Addin
#addin nuget:?package=TypedWorkflow&version=1.4.0

// Install TypedWorkflow as a Cake Tool
#tool nuget:?package=TypedWorkflow&version=1.4.0                

TypedWorkflow Logo

General description

Allows you to create a simple workflow structure from specially marked up handler methods, in which the input and output types are the links between the work items.

This implementation is an IoC framework that allows you to execute a sequence of specially marked up class methods, in the order determined by the input and output value types of those methods.

public class SimpleComponent
{
    [TwEntrypoint]
    public int FirstRun()
        => 1;

    [TwEntrypoint]
    public float SecondRun(int i)
        => (float)i;
}

public class OtherSimpleComponent
{
    [TwEntrypoint]
    public void ThirdRun(float i){}
}

For such a system to work, one mandatory rule must be observed - uniqueness of the return type of the handler method.

The following code cannot be executed:

public class SimpleComponent
{
    [TwEntrypoint]
    public int Run()
        => 1;
}
public class OtherSimpleComponent
{
    [TwEntrypoint]
    public int Run()
        => 2;
}

The second mandatory rule is that - all return types must be in the input parameters of the handler methods.

When trying to execute this schema, the framework will throw an exception due to the presence of an unused int parameter

public class SimpleComponent
{
    [TwEntrypoint]
    public (int, float) Run()
        => (1, 2.0);
}
public class OtherSimpleComponent
{
    [TwEntrypoint]
    public void Run(float i){}
}

Each handler can return multiple values (types) in one execution as a value tuple.

Each type in such a tuple will be considered a separate result of execution and can be used separately from neighboring types as input values to other handler methods.

Usage example

var builder = new TwContainerBuilder();
var container = builder
    .AddAssemblies(typeof(SimpleComponent).Assembly)
    .Build();
container.Run().AsTask().Wait();

This code will create a container for executing a sequence of handler methods and execute one cycle of work while waiting for the execution result. The container's Run method is thread-safe and can be used any number of times during the lifetime of the container.

Dependency Injection (DI)

Dependency injection is supported for constructors (Constructor Injection) and static methods of the TwInject tagged classes that contain the tagged handler methods. This can be done through the implementation of the TypedWorkflow.IResolver interface and passing an instance of such a class when creating a container.

var resolver = new MyDiResolver();
var builder = new TwContainerBuilder();
var container = builder
    .AddAssemblies(typeof(SimpleComponent).Assembly)
    .RegisterExternalDi(resolver)
    .Build();

Singleton components

It is possible to create static handler methods

public static class SingletonComponent
{
    [TwEntrypoint]
    public static void Run(int i, float f){}
}

Passing parameters from the external environment

At each cycle of the container, an arbitrary number of values can be entered into the system

var builder = new TwContainerBuilder();
var container = builder
    .AddAssemblies(typeof(SimpleComponent).Assembly)
    .Build<(int, float)>();
container.Run((1, 2.0)).AsTask().Wait();

It is necessary that at least one handler method has a dependence on the input values.

public class SimpleComponent
{
    [TwEntrypoint]
    public void Run(int i, float f){}
}

Returning the results of the system operation to the external environment

Upon completion of the work, the system can generate a value with the specified type as a result of the entire system

var builder = new TwContainerBuilder();
var container = builder
    .AddAssemblies(typeof(SimpleComponent).Assembly)
    .Build<int, float>();
float result = container.Run(2).AsTask().Result;
public class SimpleComponent
{
    [TwEntrypoint]
    public float Run(int i)
        => i * 2.0;
}

Optional parameters

Skip execution of a handler method depending on the value of the result. If the result model is wrapped in a TypedWorkflow.Option wrapper, then it will allow returning an empty value as a response.

Handler methods whose model values cannot be empty (not wrapped by TypedWorkflow.Option) will be skipped if the values of these models are empty. Thus, all handler methods dependent on the execution result will be skipped.

public class SimpleComponent
{
    [TwEntrypoint]
    public TypedWorkflow.Option<int> Run()
        => TypedWorkflow.Option<float>.None;

    [TwEntrypoint]
    public float NeverRun(int i)
        => i * 2.0;

    [TwEntrypoint]
    public void NeverRun(float i) {}
}

Execution constraints

They are implemented as the TwConstraintAttribute attribute and are intended to block the execution of a handler method based on the presence or absence of a given model value. Using such restrictions, it is possible to implement the operation of the cache, this is when the presence of a value in the cache blocks the execution of an operation to obtain this value from its slower storage (for example, a database). A constraint condition can be assigned both to the base or main class of the component, and to the handler method itself. The result of a blocked handler method will have an empty value, so all handler methods dependent on this value will be skipped and their results will also have an empty value.

[TwConstraint(typeof(FromCache), HasNone = true)]
public class ConstrainedComponent
{
    [TwEntrypoint]
    public Option<FromDb> GetModelFromDb()
    {
        return Option.Create(new FromDb(new SomeModel()));
    }
}

A more detailed example can be found in the tests TypedWorkflowTests.Components.11-ConstrainedComponent.cs

Performance issues

  • Return instances of classes, not structures (the mechanism of boxing and unboxing in cases of calling handler methods through the use of reflection nullifies all the advantages of structures).
  • Use singlеton components whenever possible (using static methods as handler methods).

Working use cases

Can be found in the project TypedWorkflowTests in the class WorkflowBuilderTest (all components are located in the folder Components of the specified project)

Credits

Icons made by Freepik from www.flaticon.com

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.5.2 444 1/14/2021
1.5.1 493 12/24/2020 1.5.1 is deprecated because it has critical bugs.
1.5.0 471 12/20/2020 1.5.0 is deprecated because it has critical bugs.
1.4.0 419 11/23/2020
1.3.0 396 11/19/2020
1.2.1 424 11/2/2020
1.2.0 397 10/28/2020
1.0.0 419 9/28/2020