Magnett.Automation.Core
1.0.0
dotnet add package Magnett.Automation.Core --version 1.0.0
NuGet\Install-Package Magnett.Automation.Core -Version 1.0.0
<PackageReference Include="Magnett.Automation.Core" Version="1.0.0" />
<PackageVersion Include="Magnett.Automation.Core" Version="1.0.0" />
<PackageReference Include="Magnett.Automation.Core" />
paket add Magnett.Automation.Core --version 1.0.0
#r "nuget: Magnett.Automation.Core, 1.0.0"
#:package Magnett.Automation.Core@1.0.0
#addin nuget:?package=Magnett.Automation.Core&version=1.0.0
#tool nuget:?package=Magnett.Automation.Core&version=1.0.0
magnett automation
Library designed for create custom workflow, orchestration between components, microservices and other automation utilities, created on dotnet version 6
Introduction
This library has been designed to have an ecosystem of entities oriented to the orchestration of any type of process. From the simplest processes such as calls between several of our classes to more complex processes such as calls to microservices, etc. Always, however, declaratively and looking to save code in terms of tedious and complex nesting of “If” “else”... etc. Structure
In this repository, there will be the core classes of this ecosystem, grouped under the namespace magnett.automation.core. Some of these classes can be used outside the scope of creating a workflow, as they are generic enough to be useful independently.
- Common
- Context.
- StateMachine.
- Workflows.
Common
In this namespace we found utilities' class used inside the library classes
- Enumeration
- CommonNamedKey
- DictionaryWrapper
Context
The concept context is very generic and is used in several fields. For us, a context will be a common space where values are stored to be shared between several components, being that they are values of the heterogeneous type.
We are, therefore, in front of a key/value system, where the values will be of any type.
Structure
The structure of the context is straightforward, it is formed only by the Context class, which will be our input and retrieval of values class, and the IContextVault interface which will be the definition of the vault where the values are stored.
By default, we will have an implementation of the IContextVault, where it will store the values in memory, but will be open for any other implementation that stores these values in any other way.
We will use the class ContextField to Get and Set values in a context, with this class we can define the type and the name of the class.
Example of how to Get / Set value in a context
var context = Context.Create();
var field = ContextField<int>.Create("FieldName");
//Set Value
context.Store(field, random.Next(1000));
//Get Value
var value = context.Value(field);
StateMachine
We have at our disposal several interfaces for the definition of a state machine as well as its execution. The definition of the machine will be separated from the execution of the machine itself, to avoid couplings and clear separation of responsibilities.
Structure
The main interface is IMachine. With this interface, we will have access to the current state, IState interface, and the possibility of transitioning to another state using action codes that generate a transition, ITransaction entity, to another state.
It is not possible to go from one state to another directly, only through a transition, so that we have a model to which states we can go from one in particular.
A state without defined transitions can be given, and this means that the state is terminal. In this way, we can define finite or non-finite machines.
Regarding the runtime part, the definition of a machine will be done from the IMachineDefinition interface, which will be generated from the MachineDefinitionBuilder class.
Example of machine definition code
//Helper class with states enumeration
public class State : Enumeration
{
public static readonly State Init = new State(1, nameof(Init));
public static readonly State Working = new State(2, nameof(Working));
public static readonly State Paused = new State(3, nameof(Paused));
public static readonly State Finished = new State(4, nameof(Finished));
private State(int id, string name) : base(id, name)
{
}
}
//Helper class with action enumerations
public class Action : Enumeration
{
public static readonly Action Start = new Action(1, nameof(Start));
public static readonly Action Pause = new Action(2, nameof(Pause));
public static readonly Action Continue = new Action(3, nameof(Continue));
public static readonly Action Finish = new Action(4, nameof(Finish));
private Action(int id, string name) : base(id, name)
{
}
}
//Now we can create a definition
_definition = MachineDefinitionBuilder.Create()
.InitialState(State.Init)
.OnAction(Action.Start).ToState(State.Working)
.Build()
.AddState(State.Working)
.OnAction(Action.Pause).ToState(State.Paused)
.OnAction(Action.Finish).ToState(State.Finished)
.Build()
.AddState(State.Paused)
.OnAction(Action.Continue).ToState(State.Working)
.Build()
.AddState(State.Finished)
.Build()
.BuildDefinition();
Example of machine creation and usage code.
var machine = Machine
.Create(SimpleMachineDefinition.GetDefinition());
machine.Dispatch(Action.Start);
var currentState = machine.State;
Workflows
Under this namespace, we will have the necessary classes to define a workflow and execute it. As in the previous section, we will keep the workflow definition separate from the runtime.
Structure
This separation will be done using the IWorkflowDefinition and IWorkflowRunner interfaces.
To encapsulate the definition and execution we have the IFlow interface, this interface also will allow us in the future to build sub-flows, create flows that are encapsulated as a service within more complex applications... etc.
If we think of a basic flow,just and initial node to reset field values, the next node just to calculate to random numbers, and a final node to sum both values, the definition should be something like that.
Example workflow definition code.
var contextDefinition = ContextDefinition.Create();
var definition = FlowDefinitionBuilder.Create()
.WithInitialNode<ResetValue>(NodeName.Reset)
.OnExitCode(ResetValue.ExitCode.Ok).GoTo(NodeName.SetValue)
.Build()
.WithNode<SetValue>(NodeName.SetValue)
.OnExitCode(SetValue.ExitCode.Assigned).GoTo(NodeName.SumValue)
.Build()
.WithNode<SumValue>(NodeName.SumValue)
.Build()
.BuildDefinition();
Previously, you have defined some helper classes like ContextDefinition it's just a static class to contain Context field and to avoid duplication and mistakes with name definitions.
internal static class ContextDefinition
{
public static ContextField<int> FirstDigit => ContextField<int>.Create("FieldOne");
public static ContextField<int> SecondDigit => ContextField<int>.Create("FieldTwo");
public static ContextField<int> Result => ContextField<int>.Create("FieldResult");
}
We have two node types sync and async, under the INode and INodeAsync interfaces, so we can use nodes as a wrapper of both types of process.
The library provides a base class for each type of node, Node and NodeAsync respectively, so we can implement our custom nodes.
In this example, we have only the sync implementation.
Example Node
internal class ResetValue : Node
{
#region ExitCodes
public class ExitCode : Enumeration
{
public static readonly ExitCode Ok = new ExitCode(1, "Ok");
private ExitCode(int id, string name) : base(id, name)
{
}
}
#endregion
public ResetValue(CommonNamedKey key) : base(key)
{
}
public override NodeExit Execute(Context context)
{
context.Store(ContextDefinition.FirstDigit, 0);
context.Store(ContextDefinition.SecondDigit, 0);
context.Store(ContextDefinition.Result, 0);
return NodeExit.Create(ExitCode.Ok.Name);
}
}
The inner class ExitCodes is just another helper class, build over Enumeration class with the definition of available exit codes for this node, we use something similar
A runner, to instantiate itself, will need to receive the workflow definition and a context instance that will be used to share information between nodes. Once the runner has been executed, we can retrieve return values from context if there are any.
We have the abstract class FlowRunnerBase so we can implement our custom runners, step to step, distributed, etc.
Example Flow runner
var flowRunner = FlowRunner.Create(definition, Context.Create());
var exit = await flowRunner.Start();
The class flow, as we said before, it's a wrapper for all this process, now has basic functionalities but in future versions will be used as the main class for workflow management.
var definition = SimpleFlowDefinition.GetDefinition();
var context = Context.Create();
var flow = Flow.Create(FlowRunner.Create(definition, context));
var exit = await flow.Run();
Thanks for reading, and I hope you find this library useful. Feedback is always welcome.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. 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 is compatible. 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 is compatible. 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. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net6.0
- Microsoft.Extensions.Logging (>= 8.0.0)
- Microsoft.Extensions.Logging.Console (>= 8.0.0)
-
net7.0
- Microsoft.Extensions.Logging (>= 8.0.0)
- Microsoft.Extensions.Logging.Console (>= 8.0.0)
-
net8.0
- Microsoft.Extensions.Logging (>= 8.0.0)
- Microsoft.Extensions.Logging.Console (>= 8.0.0)
-
net9.0
- Microsoft.Extensions.Logging (>= 8.0.0)
- Microsoft.Extensions.Logging.Console (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v.0.5.1
- Initial serious release
v.0.6.0
- Split Runtime from Definition in Workflow declaration
- Add support for net Core 8.
v.1.0.0
- Definition of Workflow does not require an instance, just using generic type
- Total separation between Definition and Runtime
- Minor changes in runtime to increase performance
- Add support for net Core 9.0