Operation Dependencies¶
As your pipeline grows and you add more steps to the logic in the form of Operations and Async Operations you may encounter situations where new business requirements must be implemented.
Implementing new requirements in the form of new Operations will require you to place the execution of the newly developed Operation somewhere in the pipeline sequence. Sometimes the implementation of new requirements will require the moving of existing Operations up or down in the pipeline order. While this re-ordering of Operations is very easy when using this framework, it gives rise to the following errors.
Moving an existing Operation up in the pipeline may cause you to place it above another Operation that is supposed to be executed before it
Moving an existing Operation down in the pipeline may cause you to place it below another Operation that needs to be executed after it
A new Operation can be added to the pipeline above another Operation that needs to be executed before it
When one Operation expects another Operation to have been executed before it, then this constitutes an Operation-to-Operation dependency. Over time, it becomes difficult to ascertain how far up or down in the pipeline is safe to execute an Operation. When an Operation expects multiple Operations to have been executed before it, then it gets even more difficult to manage the order of Operations in the pipeline.
Operation Dependency Resolution¶
When creating an Operation, you must ascertain what other Operation(s) if any must have been executed before it. If one or more
Operations must execute before the current Operation, then you should mark those dependencies by adding the Operation marker
interface types to the Operation’s Dependencies
collection in its constructor.
The code below illustrates this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | using KnightMoves.Pipelines;
using KnightMoves.Pipelines.Interfaces;
namespace MyApplication.Operations
{
// Marker Interface
public interface IFilterCustomersOperations : IPipelineOperation<MyApplicationContext> { }
public class FilterCustomersOperation : BasePipelineOperation<MyApplicationContext>, IFilterCustomersOperations
{
public override void Execute(MyApplicationContext context)
{
// In order for this to work, some other Operation must have been executed
// to fetch the Customers and plant them in the Context.Customers collection.
// If the Customers collection has not been initialized and/or populated, then
// this code will break
context.EmailCampaignCustomers = context.Customers.Where(c => c.Region == Regions.Midwest);
}
}
}
|
In order to safely place this Operation somewhere in the pipeline, you can mark its dependencies like so.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | using KnightMoves.Pipelines;
using KnightMoves.Pipelines.Interfaces;
namespace MyApplication.Operations
{
// Marker Interface
public interface IFilterCustomersOperations : IPipelineOperation<MyApplicationContext> { }
public class FilterCustomersOperation : BasePipelineOperation<MyApplicationContext>, IFilterCustomersOperations
{
public FilterCustomersOperation()
{
// Dependency added here
Dependencies.Add(typeof(IFetchCustomersOperationAsync));
}
public override void Execute(MyApplicationContext context)
{
context.EmailCampaignCustomers = context.Customers.Where(c => c.Region == Regions.Midwest);
}
}
}
|
Here you are notifying the Pipeline Coordinator that this Operation cannot be placed above IFetchCustomersOperationAsync
in the pipeline order. If this Operation is moved above IFetchCustomersOperationAsync
, then the Pipeline Coordinator
will throw an OperationDependencyNotExecutedException
at runtime.
The unit test for the class that uses the Pipeline Coordinator should throw this exception or it will be thrown the very first time you run the application. In this way, you are guaranteed the safety of being notified that an Operation-to-Operation dependency was not satisfied and you will have to resolve the dependency by moving one of the Operations up or down to ensure dependent Operations are executed first.