Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace IsDisposable with Dispose + Refactor #27

Merged
merged 12 commits into from
Nov 4, 2024
29 changes: 12 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ You can even define a fallback that will be executed after your entire chain:
var exceptionHandlersChain = new ResponsibilityChain<Exception, bool>(new ActivatorMiddlewareResolver())
.Chain<OutOfMemoryExceptionHandler>() // The order of middleware being chained matters
.Chain<ArgumentExceptionHandler>()
.Finally<FinallyDoSomething>();
.Finally<ExceptionHandlerFallback>();

public class FinallyDoSomething : IFinally<Exception, bool>
public class ExceptionHandlerFallback : IFinally<Exception, bool>
{
public bool Finally(Exception parameter)
{
Expand All @@ -105,7 +105,7 @@ var result = exceptionHandlersChain.Execute(new InvalidOperationException()); //
```
The result will be true because of the type used in the `Finally` method.

You can also choose to throw an exception in the `Finally` method instead of returning a value:
You can also choose to throw an exception in the finally instead of returning a value:
```C#
var exceptionHandlersChain = new ResponsibilityChain<Exception, bool>(new ActivatorMiddlewareResolver())
.Chain<OutOfMemoryExceptionHandler>()
Expand Down Expand Up @@ -202,9 +202,9 @@ If you want to, you can use the asynchronous version, using asynchronous middlew
var exceptionHandlersChain = new AsyncResponsibilityChain<Exception, bool>(new ActivatorMiddlewareResolver())
.Chain<OutOfMemoryAsyncExceptionHandler>() // The order of middleware being chained matters
.Chain<ArgumentAsyncExceptionHandler>()
.Finally<ExceptionHandlerFallback>();
.Finally<ExceptionHandlerAsyncFallback>();

public class ExceptionHandlerFallback : IAsyncFinally<Exception, bool>
public class ExceptionHandlerAsyncFallback : IAsyncFinally<Exception, bool>
{
public Task<bool> Finally(Exception parameter)
{
Expand Down Expand Up @@ -234,27 +234,22 @@ var pipeline = new AsyncPipeline<Bitmap>(new ActivatorMiddlewareResolver())
.Add<AddTransparencyAsyncMiddleware>() // You can mix both kinds of asynchronous middleware
.AddCancellable<AddWatermarkCancellableAsyncMiddleware>();

Bitmap image = (Bitmap) Image.FromFile("party-photo.png");
CancellationToken cancellationToken = CancellationToken.None;

Bitmap image = (Bitmap) Image.FromFile("party-photo.png");
await pipeline.Execute(image, cancellationToken);

public class RoudCornersCancellableAsyncMiddleware : ICancellableAsyncMiddleware<Bitmap>
{
public async Task Run(Bitmap parameter, Func<Bitmap, Task> next, CancellationToken cancellationToken)
{
await RoundCournersAsync(parameter, cancellationToken);
await next(parameter);
}

private async Task RoudCournersAsync(Bitmap bitmap, CancellationToken cancellationToken)
{
// Handle somehow
await Task.CompletedTask;
await next(parameter);
}
}
```
And to pass the cancellation token to your asynchronous chain of responsibility middleware, you can implement the `ICancellableAsyncMiddleware<TParameter, TReturn>` interface
and pass the cancellation token argument to the `IAsynchChainOfResponsibility<TParamete, TReturnr>.Execute` method.
and pass the cancellation token argument to the `IAsynchChainOfResponsibility<TParamete, TReturn>.Execute` method.

## Middleware resolver
You may be wondering what is all this `ActivatorMiddlewareResolver` class being passed to every instance of pipeline and chain of responsibility.
Expand Down Expand Up @@ -325,7 +320,7 @@ public class RoudCornersAsyncMiddleware : IAsyncMiddleware<Bitmap>
}
```

Or instantiate pipeline/chain of responsibility directly:
Or instantiate it directly:
```C#
services.AddMiddlewareFromAssembly(typeof(RoudCornersAsyncMiddleware).Assembly);

Expand Down Expand Up @@ -379,9 +374,9 @@ With:
var exceptionHandlersChain = new ResponsibilityChain<Exception, bool>(new ActivatorMiddlewareResolver())
.Chain<OutOfMemoryExceptionHandler>()
.Chain<ArgumentExceptionHandler>()
.Finally<FinallyDoSomething>();
.Finally<ExceptionHandlerFallback>();

public class FinallyDoSomething : IFinally<Exception, bool>
public class ExceptionHandlerFallback : IFinally<Exception, bool>
{
public bool Finally(Exception parameter)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public void Resolve_ResolvesParameterlessConstructorMiddleware()
var resolverResult = resolver.Resolve(typeof(ParameterlessConstructorMiddleware));

Assert.NotNull(resolverResult.Middleware);
Assert.False(resolverResult.IsDisposable);
Assert.True(resolverResult.Dispose);
}

[Fact]
Expand All @@ -139,7 +139,7 @@ public void Resolve_ResolvesTransientMiddleware()
var resolverResult = resolver.Resolve(typeof(TransientMiddleware));

Assert.NotNull(resolverResult.Middleware);
Assert.False(resolverResult.IsDisposable);
Assert.True(resolverResult.Dispose);
}

[Fact]
Expand All @@ -154,7 +154,7 @@ public void Resolve_ResolvesScopedMiddleware()
var resolverResult = resolver.Resolve(typeof(ScopedMiddleware));

Assert.NotNull(resolverResult.Middleware);
Assert.False(resolverResult.IsDisposable);
Assert.True(resolverResult.Dispose);
}

[Fact]
Expand All @@ -168,7 +168,7 @@ public void Resolve_ResolvesSingletonMiddleware()
var resolverResult = resolver.Resolve(typeof(SingletonMiddleware));

Assert.NotNull(resolverResult.Middleware);
Assert.False(resolverResult.IsDisposable);
Assert.True(resolverResult.Dispose);
}

[Fact]
Expand All @@ -182,7 +182,7 @@ public void Resolve_ResolvesDisposableMiddleware()
var resolverResult = resolver.Resolve(typeof(DisposableMiddleware));

Assert.NotNull(resolverResult.Middleware);
Assert.True(resolverResult.IsDisposable);
Assert.True(resolverResult.Dispose);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public void Resolve_ResolvesParameterlessConstructorMiddleware()
var resolverResult = resolver.Resolve(typeof(ParameterlessConstructorMiddleware));

Assert.NotNull(resolverResult.Middleware);
Assert.False(resolverResult.IsDisposable);
Assert.False(resolverResult.Dispose);
}

[Fact]
Expand All @@ -120,7 +120,7 @@ public void Resolve_ResolvesTransientMiddleware()
var resolverResult = resolver.Resolve(typeof(TransientMiddleware));

Assert.NotNull(resolverResult.Middleware);
Assert.False(resolverResult.IsDisposable);
Assert.False(resolverResult.Dispose);
}

[Fact]
Expand All @@ -136,7 +136,7 @@ public void Resolve_ResolvesScopedMiddleware()
var resolverResult = resolver.Resolve(typeof(ScopedMiddleware));

Assert.NotNull(resolverResult.Middleware);
Assert.False(resolverResult.IsDisposable);
Assert.False(resolverResult.Dispose);
}

[Fact]
Expand All @@ -151,7 +151,7 @@ public void Resolve_ResolvesSingletonMiddleware()
var resolverResult = resolver.Resolve(typeof(SingletonMiddleware));

Assert.NotNull(resolverResult.Middleware);
Assert.False(resolverResult.IsDisposable);
Assert.False(resolverResult.Dispose);
}

[Fact]
Expand All @@ -166,7 +166,7 @@ public void Resolve_ResolvesDisposableMiddleware()
var resolverResult = resolver.Resolve(typeof(DisposableMiddleware));

Assert.NotNull(resolverResult.Middleware);
Assert.False(resolverResult.IsDisposable);
Assert.False(resolverResult.Dispose);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public MyService(IAsyncPipeline <Bitmap> pipeline)
public async Task DoSomething()
{
Bitmap image = (Bitmap) Image.FromFile("party-photo.png");

await _pipeline.Execute(image);
}
}
Expand Down Expand Up @@ -149,7 +148,7 @@ public ServiceCollectionExtensionsTests(ITestOutputHelper output)
}

[Fact]
public async Task AddPipelineNet_Works_Readme()
public async Task AddMiddlewareFromAssembly_Works()
{
var serviceProvider = new ServiceCollection()
.AddMiddlewareFromAssembly(typeof(RoudCornersAsyncMiddleware).Assembly)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,12 @@ public ActivatorUtilitiesMiddlewareResolver(IServiceProvider serviceProvider)
public MiddlewareResolverResult Resolve(Type type)
{
var middleware = ActivatorUtilities.CreateInstance(_serviceProvider, type);
bool isDisposable = middleware is IDisposable
#if NETSTANDARD2_1_OR_GREATER
|| middleware is IAsyncDisposable
#endif
;
bool dispose = true;

return new MiddlewareResolverResult()
{
Middleware = middleware,
IsDisposable = isDisposable
Dispose = dispose
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ public ServiceProviderMiddlewareResolver(IServiceProvider serviceProvider)
public MiddlewareResolverResult Resolve(Type type)
{
var middleware = _serviceProvider.GetRequiredService(type);
bool isDisposable = false;
bool dispose = false;

return new MiddlewareResolverResult()
{
Middleware = middleware,
IsDisposable = isDisposable
Dispose = dispose
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\PipelineNet\PipelineNet.csproj" PrivateAssets="compile;contentfiles;build;analyzers" />
<ProjectReference Include="..\PipelineNet\PipelineNet.csproj" />
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this as it forces me to install both packages separately.

</ItemGroup>

<ItemGroup>
Expand Down
36 changes: 35 additions & 1 deletion src/PipelineNet/AsyncBaseMiddlewareFlow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;

namespace PipelineNet
{
Expand Down Expand Up @@ -46,7 +47,7 @@ internal AsyncBaseMiddlewareFlow(IMiddlewareResolver middlewareResolver)
/// </summary>
/// <param name="middlewareType">The middleware type to be executed.</param>
/// <exception cref="ArgumentException">Thrown if the <paramref name="middlewareType"/> is
/// not an implementation of <typeparamref name="TMiddleware"/> or <see cref="TCancellableMiddleware"/>.</exception>
/// not an implementation of <typeparamref name="TMiddleware"/> or <typeparamref name="TCancellableMiddleware"/>.</exception>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="middlewareType"/> is null.</exception>
protected void AddMiddleware(Type middlewareType)
{
Expand All @@ -60,5 +61,38 @@ protected void AddMiddleware(Type middlewareType)

this.MiddlewareTypes.Add(middlewareType);
}

internal void EnsureMiddlewareNotNull(MiddlewareResolverResult middlewareResolverResult, Type middlewareType)
{
if (middlewareResolverResult == null || middlewareResolverResult.Middleware == null)
{
throw new InvalidOperationException($"'{MiddlewareResolver.GetType()}' failed to resolve middleware of type '{middlewareType}'.");
}
}

internal static async Task DisposeMiddlewareAsync(MiddlewareResolverResult middlewareResolverResult)
{
if (middlewareResolverResult != null && middlewareResolverResult.Dispose)
{
var middleware = middlewareResolverResult.Middleware;
if (middleware != null)
{
#if NETSTANDARD2_1_OR_GREATER
if (middleware is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync().ConfigureAwait(false);
}
else
mariusz96 marked this conversation as resolved.
Show resolved Hide resolved
#endif
if (middleware is IDisposable disposable)
{
disposable.Dispose();
}

var completedTask = Task.FromResult(0);
await completedTask.ConfigureAwait(false);
}
}
}
}
}
30 changes: 30 additions & 0 deletions src/PipelineNet/BaseMiddlewareFlow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,35 @@ protected void AddMiddleware(Type middlewareType)

this.MiddlewareTypes.Add(middlewareType);
}

internal void EnsureMiddlewareNotNull(MiddlewareResolverResult middlewareResolverResult, Type middlewareType)
{
if (middlewareResolverResult == null || middlewareResolverResult.Middleware == null)
{
throw new InvalidOperationException($"'{MiddlewareResolver.GetType()}' failed to resolve middleware of type '{middlewareType}'.");
}
}

internal static void DisposeMiddleware(MiddlewareResolverResult middlewareResolverResult)
{
if (middlewareResolverResult != null && middlewareResolverResult.Dispose)
{
var middleware = middlewareResolverResult.Middleware;
if (middleware != null)
{
if (middleware is IDisposable disposable)
{
disposable.Dispose();
}
#if NETSTANDARD2_1_OR_GREATER
else if (middleware is IAsyncDisposable)
{
throw new InvalidOperationException($"'{middleware.GetType()}' type only implements IAsyncDisposable." +
" Use AsyncPipeline/AsyncResponsibilityChain to execute the configured pipeline/chain fo responsibility.");
}
#endif
}
}
}
}
}
Loading
Loading