Documentation Home

Operations

Operations are where the logic of the pipeline reside. Here we will be discussing non-async Operations. You should create this type of Operation to implement logic that does not do anything that would merit asynchronous execution.

Warning

Don’t use a non-async Operation when its logic

  • Executes a long running calculation

  • Uses File System IO

  • Communicates over the network (e.g. REST API calls, RPC, print jobs, etc.)

  • Reads / Writes from/to a database

To create a non-async Operation follow the steps below.

Steps to Create an Operation

Pre-Requisite

You must have created the Pipeline Context object that your Operation will use as a state object. The Pipeline Coordinator will take care of injecting that state object into the Operation’s Execute(TContext) method.

Step 1: Add New Class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
using System;
using System.Collections.Generic;
using System.Text;

namespace MyApplication.Operations
{
    public class MyOperation
    {
    }
}

Note

It is recommended that you suffix the class with “Operation” as a naming convention.

Step 2: Create Marker Interface

Create a marker interface that inherits from IPipelineOperation<TContext> and specify the type of the application’s Pipeline Context state object that this Operation will handle as its TContext.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
using using KnightMoves.Pipelines.Interfaces;

namespace MyApplication.Operations
{
    // Marker Interface
    public interface IMyOperation : IPipelineOperation<MyApplicationContext> { }

    public class MyOperation
    {
    }
}

Step 3: Inherit and Implement

Inherit from BasePipelineOperation and implement the IMyOperation marker interface.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
using KnightMoves.Pipelines;
using KnightMoves.Pipelines.Interfaces;

namespace MyApplication.Operations
{
    // Marker Interface
    public interface IMyOperation : IPipelineOperation<MyApplicationContext> { }

    public class MyOperation : BasePipelineOperation<MyApplicationContext>, IMyOperation
    {
    }
}

Step 4: Implement Operation Logic

Implement the IPipelineOperation<TContext>`’s `Execute(TContext)` method.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
using KnightMoves.Pipelines;
using KnightMoves.Pipelines.Interfaces;

namespace MyApplication.Operations
{
    // Marker Interface
    public interface IMyOperation : IPipelineOperation<MyApplicationContext> { }

    public class MyOperation : BasePipelineOperation<MyApplicationContext>, IMyOperation
    {
        public override void Execute(MyApplicationContext context)
        {
            // Logic goes here
        }
    }
}

Warning

If your Operation requires that another Operation be executed before it in the pipeline, then this is an Operation-to-Operation dependency and you should add those dependencies to the Dependencies collection in the Operation’s constructor.

See the documentation for this here

Using the Pipeline Context

Successful

The Pipeline Context object contains a boolean property called Successful documented in the Pipeline Context page. You can examine this property to make a decision on whether or not to do something.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// removed outer code blocks for brevity

    public override void Execute(MyApplicationContext context)
    {
        if(!context.Successful)
        {
            // Do nothing
            return;
        }

        // Logic goes here
    }

EndProcessing

You can cancel the execution of the rest of the pipeline by setting the EndProcessing property to true. The Pipeline Coordinator will not execute any Operation in the pipeline if this is set to true.

1
2
3
4
5
6
7
8
9
// removed outer code blocks for brevity

    public override void Execute(MyApplicationContext context)
    {
        // Logic here resulted in some critical failure so we terminate
        // the execution of all other Operations after this

        context.EndProcessing = true;
    }

ResultMessages

You can (and should) report the result of the Operation’s execution by putting a message in the ResultMessages collection. It can then be used at the end of the pipeline execution for logging and debugging.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// removed outer code blocks for brevity

    public override void Execute(MyApplicationContext context)
    {
        var okay = true;

        // Logic goes here and sets okay to false if something went wrong

        if(!okay)
        {
            context.ResultMessages.Add("MyOperation Failed!");
            return;
        }

        context.ResultMessages.Add("MyOperation Successfully executed!");
    }

Later the Pipeline Context can be used for logging and debugging.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public static void Main(string[] args)
{
   // ...

   _pipelineCoordinator
       .Execute<IMyOperation>()
       .Execute<ISaveResults>()
   ;

   LogOperationResults(_pipelineCoordinator.Context.ResultMessages);

   // ...

}

private static void LogOperationResults(IList<string> results)
{
    // Log results here
}

Exceptions

If exceptions are caught in the Operation’s logic and you want to gracefully handle them in a try/catch block, then you can plant the exception in the Exceptions collection of the Pipeline Context for logging and debugging later.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// removed outer code blocks for brevity

    public override void Execute(MyApplicationContext context)
    {
        try
        {
            // Some logic goes here
        }
        catch(Exception ex)
        {
            // Doh! Exception!
            context.Exceptions.Add(ex);
            context.EndProcessing = true;
            context.ResultMessages.Add("MyOperation Exception: " + ex.Message);
            return;
        }

        // Rest of Logic goes here

        context.ResultMessages.Add("MyOperation Successfully executed!");
    }

Later the Pipeline Context can be used for logging and debugging.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public static void Main(string[] args)
{
   // ...

   _pipelineCoordinator
       .Execute<IMyOperation>()
       .Execute<ISaveResults>()
   ;

   LogExceptions(_pipelineCoordinator.Context.Exceptions);

   // ...

}

private static void LogExceptions(IList<Exception> results)
{
    // Log results here
}