A simple and innovative library to implement chain of responsibilities. To learn more about chain of responsibilities pattern, read the following article https://refactoring.guru/design-patterns/chain-of-responsibility
- Written in .NET 7
- No Dependencies
- No Reflection
- Easy to Use
Depending on your usage, follow one of the guidelines below.
Install with NuGet:
Install-Package ChainRunner
Install-Package ChainRunner.Extensions.MicrosoftDependencyInjection
or with .NET CLI:
dotnet add package ChainRunner
dotnet add package ChainRunner.Extensions.MicrosoftDependencyInjection
and configure your desired as below in the ConfigureServices
method of Startup.cs
:
services.AddChain<ChainRequest>()
.WithHandler<ResponsibilityHandler1>()
.WithHandler<ResponsibilityHandler2>()
.WithHandler<ResponsibilityHandler3>();
Consider the following example and follow the sections in order:
You intend to execute a series of actions (i.e., PersistUserData, SubscribeUserToNewsletter, SendWelcomeEmail) when a new user signs up into system. First, you create a class the contains all the necessary information to executes these actions.
public class CompleteRegistrationRequest
{
public string UserId { get; set; }
}
Then, for each action you create class that implements IResponsibilityHandler
interface.
public class PersistUserDataHandler : IResponsibilityHandler<CompleteRegistrationRequest>
{
public Task HandleAsync(SendNotificationRequest request, CancellationToken cancellationToken = default)
{
// implementation goes here ...
}
}
public class SubscribeUserToNewsletterHandler : IResponsibilityHandler<CompleteRegistrationRequest>
{
public Task HandleAsync(SendNotificationRequest request, CancellationToken cancellationToken = default)
{
// implementation goes here ...
}
}
public class SendWelcomeEmailHandler : IResponsibilityHandler<CompleteRegistrationRequest>
{
public Task HandleAsync(SendNotificationRequest request, CancellationToken cancellationToken = default)
{
// implementation goes here ...
}
}
Setup your chain in the ConfigureServices
method of Startup.cs
services.AddChain<CompleteRegistrationRequest>()
.WithHandler<PersistUserDataHandler>()
.WithHandler<SubscribeUserToNewsletterHandler>()
.WithHandler<SendWelcomeEmailHandler>();
Inject your chain to your desired class and run it
[ApiController]
[Route("[controller]")]
public class Controller
{
private readonly IChain<CompleteRegistrationRequest> _chain;
public Controller(IChain<CompleteRegistrationRequest> chain)
{
_chain = chain;
}
[HttpPost]
public async Task<IActionResult> Register(CompleteRegistrationRequest request)
{
await _chain.RunAsync(request);
return Ok();
}
}
Inject IChainBuilder
to your desired class and setup you chain.
[ApiController]
[Route("[controller]")]
public class Controller
{
private readonly IChainBuilder _chainBuilder;
public Controller(IChainBuilder chainBuilder)
{
_chainBuilder = chainBuilder;
}
[HttpPost]
public async Task<IActionResult> Register(CompleteRegistrationRequest request)
{
var chain = _chainBuilder.For<CompleteRegistrationRequest>()
.WithHandler<PersistUserDataHandler>()
.WithHandler<SubscribeUserToNewsletterHandler>()
.WithHandler<SendWelcomeEmailHandler>()
.Build();
await chain.RunAsync(request);
}
}
Before using IChainBuilder
, make sure to call AddChainRunner(assemblies)
method in ConfigureServices()
of Startup.cs
class to register IChainBuilder
and handlers.
public void ConfigureServices(IServiceCollection services)
{
services.AddChainRunner(typeof(PersistUserDataHandler).Assembly);
}
You can use the ChainBuilder<T>
class to build chains without the need of DI. The WithHandler()
method either accepts a handler with empty constructor or a pre-initialized instance of a handler.
[ApiController]
[Route("[controller]")]
public class Controller
{
[HttpPost]
public async Task<IActionResult> Register(CompleteRegistrationRequest request)
{
var chain = ChainBuilder.For<CompleteRegistrationRequest>()
.WithHandler<PersistUserDataHandler>() // pass the handler with empty constructor
.WithHandler<SubscribeUserToNewsletterHandler>() // pass the handler with empty constructor
.WithHandler(new SendWelcomeEmailHandler()) // pass the handler instance
.Build();
await chain.RunAsync(request);
}
}