FluentChaining 1.0.0
See the version list below for details.
dotnet add package FluentChaining --version 1.0.0
NuGet\Install-Package FluentChaining -Version 1.0.0
<PackageReference Include="FluentChaining" Version="1.0.0" />
paket add FluentChaining --version 1.0.0
#r "nuget: FluentChaining, 1.0.0"
// Install FluentChaining as a Cake Addin #addin nuget:?package=FluentChaining&version=1.0.0 // Install FluentChaining as a Cake Tool #tool nuget:?package=FluentChaining&version=1.0.0
FluentChaining
A lightweight library for implementing responsibility chains in fluent style.
Prerequisites
Chain of responsibility is on OOP pattern outlined by GoF.
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
–– GoF
You can learn more about that pattern on refactoring.guru website.
Examples
Creating an IChain
(IAsyncChain
)
You can create an IChain by using FluentChaining.CreateChain<TRequest, TResponse>
method
or FluentChaining.CreateChain<TRequest>
for chains that returns no value (Unit
).
If your chain links have some dependencies to resolve, you can pass down an instance
of IEnumerable<ServiceDescriptor>
, or simply an IServiceCollection
that contains these dependencies.
Overloads for chains with async
links are available as well.
public static IChain<Name, bool> CreateChain(IServiceCollection initialServices)
{
return FluentChaining.CreateChain<Name, bool>
(
initialServices,
builder => builder
.Then<LengthChecker>()
.Then<DigitCountChecker>()
.Then<SpecialSymbolsChecker>()
.FinishWith(() => true)
);
}
Registering an IChain
(IAsyncChain
) in the IServiceCollection
You can also register you chains in DI container using extension methods.
Call AddFluentChaining
method on IServiceCollection
to start registration
and configure chain creation processes (optionally).
Configuration options
- ChainLifetime
The lifetime which the created chains will be registered with (Scoped
by default). Configuring lifetime for individual chains is not supported, yet nothing stops you from callingAddFluentChaining
multiple times. - AllowDuplicates
If set totrue
, multiple chains with same (TRequest
,TResponse
) pair will be allowed, otherwise only the first one will be used.
The AddFluentChaining
method will return IChainConfigurator
instance, which has methods
for adding all types of chains.
public static void AddChainsToServiceCollection(IServiceCollection collection)
{
collection.AddFluentChaining(o =>
{
o.ChainLifetime = ServiceLifetime.Singleton;
o.AllowDuplicates = false;
})
.AddChain<Name, bool>
(
start => start
.Then<LengthChecker>()
.Then<DigitCountChecker>()
.Then<SpecialSymbolsChecker>()
.FinishWith(() => true)
)
.AddAsyncChain<Age, bool>
(
start => start
.Then<AgeChecker>()
.FinishFromResult(() => true)
);
}
Usage
To create a chain link, you simply need to implement an ILink
interface or IAsyncLink
interface (for async chains).
Links added in the chain support DI (as long as corresponding IServiceProvider
can satisfy their dependencies).
To process a request by the chain you will need to call a Process
method on the IChain
or ProcessAsync
on IAsyncChain
instance.
ILink
implementation example
public class LengthChecker : ILink<Name, bool>
{
private readonly ValidationConfiguration _configuration;
public LengthChecker(ValidationConfiguration configuration)
{
_configuration = configuration;
}
public bool Process(Name request, SynchronousContext context, LinkDelegate<Name, SynchronousContext, bool> next)
{
if (request.Value.Length < _configuration.NameLength)
return false;
return next.Invoke(request, context);
}
}
IAsyncLink
implementation example
public class AgeChecker : IAsyncLink<Age, bool>
{
private readonly ValidationConfiguration _configuration;
public AgeChecker(ValidationConfiguration configuration)
{
_configuration = configuration;
}
public Task<bool> Process(Age request, AsynchronousContext context, LinkDelegate<Age, AsynchronousContext, Task<bool>> next)
{
if (request.Value < _configuration.Age || context.CancellationToken.IsCancellationRequested)
return Task.FromResult(false);
return next.Invoke(request, context);
}
}
Chain processing example
public static bool Validate(User user, IServiceProvider provider)
{
var nameValidator = provider.GetRequiredService<IChain<Name, bool>>();
var ageValidator = provider.GetRequiredService<IAsyncChain<Age, bool>>();
return nameValidator.Process(user.Name) && ageValidator.ProcessAsync(user.Age).Result;
}
Utilising assembly scanning
Configuring links manually is definitely is a more reliable and stable approach. On the other hand it is more verbose, so for the sake of automation, or in case the link order is not important, you can use assembly scanning.
To add links from selected assemblies call ThenFromAssemblies
method on your ILinkBuilder<,,>
instance.
It receives AssemblyProvider
s as params
, it will implicitly convert instances of Type
and Assembly
objects,
courtesy of FluentScanning.
If you want to have an ease of automation that assembly scanning offers, and still have some control over the order of
links in chain,
you can use LinkPriorityAttribute
. Links will be sorted in descending order by their priority (the higher the
priority,
the earlier the link will be added), with priority 0 for ones without LinkPriorityAttribute
.
Assembly scanning example
.AddChain<Name, bool>
(
start => start
.ThenFromAssemblies(typeof(Scenario))
.FinishWith(() => true)
)
LinkPriorityAttribute example
[LinkPriority(int.MaxValue)]
public class LengthChecker : ILink<Name, bool>
{
...
}
Product | Versions 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. |
-
.NETStandard 2.0
- FluentScanning.DependencyInjection (>= 1.1.0)
- Microsoft.Extensions.DependencyInjection (>= 6.0.0)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on FluentChaining:
Package | Downloads |
---|---|
Itmo.Dev.Platform.YandexCloud
Package Description |
|
SourceKit.Generators.Builder
Type builder source generator |
|
SourceKit.Generators.Grpc
Grpc source generators |
GitHub repositories
This package is not used by any popular GitHub repositories.