Skip to content

Commit

Permalink
refactor factories
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusz96 committed Aug 1, 2024
1 parent 453f327 commit 89a2a1c
Show file tree
Hide file tree
Showing 19 changed files with 163 additions and 205 deletions.
76 changes: 26 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ dotnet add package PipelineNet
- [Middleware](#middleware)
- [Pipelines](#pipelines)
- [Chains of responsibility](#chains-of-responsibility)
- [Pipeline and Chain of responsibility factories](#pipeline-and-chain-of-responsibility-factories)
- [Middleware resolver](#middleware-resolver)
- [ServiceProvider implementation](#serviceprovider-implementation)
- [Unity implementation](#unity-implementation)
Expand Down Expand Up @@ -197,18 +196,6 @@ result = await exceptionHandlersChain.Execute(new ArgumentException()); // Resul
result = await exceptionHandlersChain.Execute(new InvalidOperationException()); // Result will be false
```

## Pipeline and Chain of responsibility factories
Instead of instantiating `IPipeline<TParamater>` and `IAsyncPipeline<TParamater>` directly, you can use `PipelineFactory<TParamater>` and `AsyncPipelineFactory<TParamater>` to instantiate them:
```C#
var pipelineFactory = new AsyncPipelineFactory<Bitmap>(new ActivatorMiddlewareResolver());

var pipeline = pipelineFactory.Create()
.Add<RoudCornersAsyncMiddleware>()
.Add<AddTransparencyAsyncMiddleware>()
.Add<AddWatermarkAsyncMiddleware>();
```
To instantiate `IResponsibilityChain<TParameter, TReturn>` and `IAsyncResponsibilityChain<TParameter, TReturn>` you can use `ResponsibilityChainFactory<TParameter, TReturn>` and `AsyncResponsibilityChainFactory<TParameter, TReturn>`.

## Middleware resolver
You may be wondering what is all this `ActivatorMiddlewareResolver` class being passed to every instance of pipeline and chain of responsibility.
This is a default implementation of the `IMiddlewareResolver`, which is used to create instances of the middleware types.
Expand All @@ -227,11 +214,27 @@ You can grab it from nuget with:
Install-Package PipelineNet.ServiceProvider
```

Use it as follows:
Instead of instantiating pipelines directly, `PipelineNet.ServiceProvider` uses factories to instantiate them:
```C#
IAsyncPipelineFactory<Bitmap> pipelineFactory = new AsyncPipelineFactory<Bitmap>();
IServiceProvider serviceProvider = GetServiceProviderSomehow();

IAsyncPipeline<Bitmap> pipeline = pipelineFactory.Create(serviceProvider)
.Add<RoudCornersAsyncMiddleware>()
.Add<AddTransparencyAsyncMiddleware>()
.Add<AddWatermarkAsyncMiddleware>();
```

It includes four types of factories:
- `PipelineFactory<TParamater>` for pipelines.
- `AsyncPipelineFactory<TParamater>` for asynchronous pipelines.
- `ResponsibilityChainFactory<TParameter, TReturn>` for chains of responsibility.
- `AsyncResponsibilityChainFactory<TParameter, TReturn>` for asynchronous chains of responsibility.

It also includes extension methods for registering factories and automatic middleware registration through assembly scanning:
```C#
services.AddPipelineNet(typeof(RoudCornersAsyncMiddleware).Assembly); // Add core PipelineNet services and all middleware from the assembly.
services.AddScoped<IMyService, MyService>();
services.AddPipelineNet(typeof(RoudCornersAsyncMiddleware).Assembly); // Add pipeline and chain of responsibility factories and all middleware from the assembly
services.AddScoped<IMyService, MyService>(); // Add service as scoped so that scoped IServiceProvider gets injected
public interface IMyService
{
Expand All @@ -241,17 +244,21 @@ public interface IMyService
public class MyService : IMyService
{
private readonly IAsyncPipelineFactory<Bitmap> _pipelineFactory;
private readonly IServiceProvider _serviceProvider;

public MyService(IAsyncPipelineFactory<Bitmap> pipelineFactory)
public MyService(
IAsyncPipelineFactory<Bitmap> pipelineFactory,
IServiceProvider serviceProvider)
{
_pipelineFactory = pipelineFactory;
_serviceProvider = serviceProvider;
}

public async Task DoSomething()
{
Bitmap image = (Bitmap) Image.FromFile("party-photo.png");

IAsyncPipeline<Bitmap> pipeline = _pipelineFactory.Create()
IAsyncPipeline<Bitmap> pipeline = _pipelineFactory.Create(_serviceProvider)
.Add<RoudCornersAsyncMiddleware>()
.Add<AddTransparencyAsyncMiddleware>()
.Add<AddWatermarkAsyncMiddleware>();
Expand All @@ -264,7 +271,7 @@ public class RoudCornersAsyncMiddleware : IAsyncMiddleware<Bitmap>
{
private readonly ILogger<RoudCornersAsyncMiddleware> _logger;

// The following constructor argument will be provided by IServiceProvider
// The following constructor arguments will be provided by the IServiceProvider
public RoudCornersAsyncMiddleware(ILogger<RoudCornersAsyncMiddleware> logger)
{
_logger = logger;
Expand All @@ -279,37 +286,6 @@ public class RoudCornersAsyncMiddleware : IAsyncMiddleware<Bitmap>
}
```

You can also instantiate the factory directly:

```C#
services.AddMidlewaresFromAssembly(typeof(RoudCornersAsyncMiddleware).Assembly); // Add all middleware from the assembly.
services.AddScoped<IMyService, MyService>();

public class MyService : IMyService
{
private readonly IServiceProvider _serviceProvider;

public MyService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}

public async Task DoSomething()
{
Bitmap image = (Bitmap) Image.FromFile("party-photo.png");

IAsyncPipelineFactory<Bitmap> pipelineFactory = new AsyncPipelineFactory<Bitmap>(new SeriveProviderMiddlewareResolver(_serviceProvider));

IAsyncPipeline<Bitmap> pipeline = pipelineFactory.Create()
.Add<RoudCornersAsyncMiddleware>()
.Add<AddTransparencyAsyncMiddleware>()
.Add<AddWatermarkAsyncMiddleware>();

await pipeline.Execute(image);
}
}
```

Note that `IServiceProvider` lifetime can vary based on the lifetime of the containing class. For example, if you resolve service from a scope, and it takes an `IServiceProvider`, it'll be a scoped instance.

For more information on dependency injection, see: [Dependency injection - .NET](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection).
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using PipelineNet.Middleware;
using PipelineNet.PipelineFactories;
using PipelineNet.Pipelines;
using PipelineNet.ServiceProvider.PipelineFactories;
using Xunit.Abstractions;

namespace PipelineNet.ServiceProvider.Tests
Expand All @@ -22,17 +22,21 @@ public interface IMyService
public class MyService : IMyService
{
private readonly IAsyncPipelineFactory<Bitmap> _pipelineFactory;
private readonly IServiceProvider _serviceProvider;

public MyService(IAsyncPipelineFactory<Bitmap> pipelineFactory)
public MyService(
IAsyncPipelineFactory<Bitmap> pipelineFactory,
IServiceProvider serviceProvider)
{
_pipelineFactory = pipelineFactory;
_serviceProvider = serviceProvider;
}

public async Task DoSomething()
{
var image = new Bitmap();
Bitmap image = new Bitmap();

IAsyncPipeline<Bitmap> pipeline = _pipelineFactory.Create()
IAsyncPipeline<Bitmap> pipeline = _pipelineFactory.Create(_serviceProvider)
.Add<RoudCornersAsyncMiddleware>()
.Add<AddTransparencyAsyncMiddleware>()
.Add<AddWatermarkAsyncMiddleware>();
Expand Down Expand Up @@ -73,7 +77,6 @@ public class RoudCornersAsyncMiddleware : IAsyncMiddleware<Bitmap>
{
private readonly ILogger<RoudCornersAsyncMiddleware> _logger;

// The following constructor argument will be provided by IServiceProvider
public RoudCornersAsyncMiddleware(ILogger<RoudCornersAsyncMiddleware> logger)
{
_logger = logger;
Expand All @@ -82,7 +85,6 @@ public RoudCornersAsyncMiddleware(ILogger<RoudCornersAsyncMiddleware> logger)
public async Task Run(Bitmap parameter, Func<Bitmap, Task> next)
{
_logger.LogInformation("Running RoudCornersAsyncMiddleware.");
// Handle somehow
await next(parameter);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using PipelineNet.ChainsOfResponsibility;
using PipelineNet.ServiceProvider.MiddlewareResolver;
using System;

namespace PipelineNet.ServiceProvider.ChainOfResponsibilityFactories
{
/// <inheritdoc/>
public class AsyncResponsibilityChainFactory<TParameter, TReturn>
: IAsyncResponsibilityChainFactory<TParameter, TReturn>
{
/// <inheritdoc/>
public IAsyncResponsibilityChain<TParameter, TReturn> Create(IServiceProvider serviceProvider)
{
if (serviceProvider == null) throw new ArgumentNullException("serviceProvider");

return new AsyncResponsibilityChain<TParameter, TReturn>(
new ServiceProviderMiddlewareResolver(serviceProvider));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using PipelineNet.ChainsOfResponsibility;
using System;

namespace PipelineNet.PipelineFactories
namespace PipelineNet.ServiceProvider.ChainOfResponsibilityFactories
{
/// <summary>
/// Used to create new instances of an asynchronous chain of responsibility.
Expand All @@ -12,7 +13,8 @@ public interface IAsyncResponsibilityChainFactory<TParameter, TReturn>
/// <summary>
/// Creates a new asynchronous chain of responsibility.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> that middlewares will be resolved from.</param>
/// <returns>A new instance of an asynchronous chain.</returns>
IAsyncResponsibilityChain<TParameter, TReturn> Create();
IAsyncResponsibilityChain<TParameter, TReturn> Create(IServiceProvider serviceProvider);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using PipelineNet.ChainsOfResponsibility;
using System;

namespace PipelineNet.PipelineFactories
namespace PipelineNet.ServiceProvider.ChainOfResponsibilityFactories
{
/// <summary>
/// Used to create new instances of a chain of responsibility.
Expand All @@ -12,7 +13,8 @@ public interface IResponsibilityChainFactory<TParameter, TReturn>
/// <summary>
/// Creates a new chain of responsibility.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> that middlewares will be resolved from.</param>
/// <returns>A new instance of a chain.</returns>
IResponsibilityChain<TParameter, TReturn> Create();
IResponsibilityChain<TParameter, TReturn> Create(IServiceProvider serviceProvider);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using PipelineNet.ChainsOfResponsibility;
using PipelineNet.ServiceProvider.MiddlewareResolver;
using System;

namespace PipelineNet.ServiceProvider.ChainOfResponsibilityFactories
{
/// <inheritdoc/>
public class ResponsibilityChainFactory<TParameter, TReturn>
: IResponsibilityChainFactory<TParameter, TReturn>
{
/// <inheritdoc/>
public IResponsibilityChain<TParameter, TReturn> Create(IServiceProvider serviceProvider)
{
if (serviceProvider == null) throw new ArgumentNullException("serviceProvider");

return new ResponsibilityChain<TParameter, TReturn>(
new ServiceProviderMiddlewareResolver(serviceProvider));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace PipelineNet.ServiceProvider.MiddlewareResolver
{
/// <summary>
/// An implementation of <see cref="IMiddlewareResolver"/> that creates
/// instances using the <see cref="Microsoft.Extensions.DependencyInjection.ActivatorUtilities"/>.
/// instances using the <see cref="ActivatorUtilities"/>.
/// </summary>
public class ActivatorUtilitiesMiddlewareResolver : IMiddlewareResolver
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ public ServiceProviderMiddlewareResolver(IServiceProvider serviceProvider)
"An instance of IServiceProvider must be provided."); ;
}

/// <inheritdoc/>
/// <summary>
/// Resolves an instance of the give middleware type.
/// </summary>
/// <param name="type">The middleware type that will be resolved.</param>
/// <returns>An instance of the middleware.</returns>
public MiddlewareResolverResult Resolve(Type type)
{
var middleware = _serviceProvider.GetRequiredService(type);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using PipelineNet.Pipelines;
using PipelineNet.ServiceProvider.MiddlewareResolver;
using System;

namespace PipelineNet.ServiceProvider.PipelineFactories
{
/// <inheritdoc/>
public class AsyncPipelineFactory<TParameter>
: IAsyncPipelineFactory<TParameter>
{
/// <inheritdoc/>
public IAsyncPipeline<TParameter> Create(IServiceProvider serviceProvider)
{
if (serviceProvider == null) throw new ArgumentNullException("serviceProvider");

return new AsyncPipeline<TParameter>(
new ServiceProviderMiddlewareResolver(serviceProvider));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using PipelineNet.Pipelines;
using System;

namespace PipelineNet.PipelineFactories
namespace PipelineNet.ServiceProvider.PipelineFactories
{
/// <summary>
/// Used to create new instances of an asynchronous pipeline.
Expand All @@ -11,7 +12,8 @@ public interface IAsyncPipelineFactory<TParameter>
/// <summary>
/// Creates a new asynchronous pipeline.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> that middlewares will be resolved from.</param>
/// <returns>A new instance of an asynchronous pipeline.</returns>
IAsyncPipeline<TParameter> Create();
IAsyncPipeline<TParameter> Create(IServiceProvider serviceProvider);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using PipelineNet.Pipelines;
using System;

namespace PipelineNet.PipelineFactories
namespace PipelineNet.ServiceProvider.PipelineFactories
{
/// <summary>
/// Used to create new instances of a pipeline.
Expand All @@ -11,7 +12,8 @@ public interface IPipelineFactory<TParameter>
/// <summary>
/// Creates a new pipeline.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> that middlewares will be resolved from.</param>
/// <returns>A new instance of a pipeline.</returns>
IPipeline<TParameter> Create();
IPipeline<TParameter> Create(IServiceProvider serviceProvider);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using PipelineNet.Pipelines;
using PipelineNet.ServiceProvider.MiddlewareResolver;
using System;

namespace PipelineNet.ServiceProvider.PipelineFactories
{
/// <inheritdoc/>
public class PipelineFactory<TParameter>
: IPipelineFactory<TParameter>
{
/// <inheritdoc/>
public IPipeline<TParameter> Create(IServiceProvider serviceProvider)
{
if (serviceProvider == null) throw new ArgumentNullException("serviceProvider");

return new Pipeline<TParameter>(
new ServiceProviderMiddlewareResolver(serviceProvider));
}
}
}
Loading

0 comments on commit 89a2a1c

Please sign in to comment.