-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4597fe3
commit a8a6bd2
Showing
8 changed files
with
196 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
src/Mediator.DependencyInjection/Pipeline/Generic/GenericPipelineBehavior.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Zapto.Mediator; | ||
|
||
internal sealed record GenericPipelineBehaviorRegistration | ||
{ | ||
public GenericPipelineBehaviorRegistration(Type requestType, Type? responseType, Type behaviorType) | ||
{ | ||
RequestType = requestType; | ||
ResponseType = responseType; | ||
BehaviorType = behaviorType; | ||
} | ||
|
||
public Type RequestType { get; } | ||
|
||
public Type? ResponseType { get; } | ||
|
||
public Type BehaviorType { get; } | ||
} | ||
|
||
internal sealed class GenericPipelineBehavior<TRequest, TResponse> | ||
where TRequest : notnull | ||
{ | ||
private readonly List<Type> _handlerTypes; | ||
private readonly IEnumerable<GenericPipelineBehaviorRegistration> _enumerable; | ||
|
||
public GenericPipelineBehavior(IEnumerable<GenericPipelineBehaviorRegistration> enumerable) | ||
{ | ||
_enumerable = enumerable; | ||
_handlerTypes = CreateHandlerTypes(); | ||
} | ||
|
||
public bool IsEmpty => _handlerTypes.Count == 0; | ||
|
||
private List<Type> CreateHandlerTypes() | ||
{ | ||
var handlerTypes = new List<Type>(); | ||
|
||
if (_enumerable is GenericPipelineBehaviorRegistration[] { Length: 0 }) | ||
{ | ||
return handlerTypes; | ||
} | ||
|
||
var requestType = typeof(TRequest); | ||
var arguments = requestType.GetGenericArguments(); | ||
|
||
if (requestType.IsGenericType) | ||
{ | ||
requestType = requestType.GetGenericTypeDefinition(); | ||
} | ||
|
||
var responseType = typeof(TResponse); | ||
|
||
if (responseType.IsGenericType) | ||
{ | ||
responseType = responseType.GetGenericTypeDefinition(); | ||
} | ||
|
||
foreach (var registration in _enumerable) | ||
{ | ||
if (!registration.RequestType.IsAssignableFrom(requestType) || | ||
registration.ResponseType is not null && !registration.ResponseType.IsAssignableFrom(responseType)) | ||
{ | ||
continue; | ||
} | ||
|
||
var type = registration.BehaviorType.MakeGenericType(arguments); | ||
|
||
handlerTypes.Add(type); | ||
} | ||
|
||
handlerTypes.Reverse(); | ||
|
||
return handlerTypes; | ||
} | ||
|
||
public ValueTask<TResponse> Handle(IServiceProvider provider, TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken) | ||
{ | ||
foreach (var cachedType in _handlerTypes) | ||
{ | ||
var behavior = (IPipelineBehavior<TRequest, TResponse>)provider.GetRequiredService(cachedType); | ||
var nextPipeline = next; | ||
|
||
next = () => behavior.Handle(provider, request, nextPipeline, cancellationToken); | ||
} | ||
|
||
return next(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
tests/Mediator.DependencyInjection.Tests/Generics/BehaviorNumberTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
#if NET7_0_OR_GREATER | ||
using System; | ||
using System.Numerics; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using MediatR; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Xunit; | ||
using Zapto.Mediator; | ||
|
||
namespace Mediator.DependencyInjection.Tests.Generics; | ||
|
||
public record struct ReturnNumberRequest<TSelf>(TSelf Value) : IRequest<TSelf> | ||
where TSelf : INumber<TSelf>; | ||
|
||
public class ReturnNumberRequestHandler<TSelf> : IRequestHandler<ReturnNumberRequest<TSelf>, TSelf> | ||
where TSelf : INumber<TSelf> | ||
{ | ||
public ValueTask<TSelf> Handle(IServiceProvider provider, ReturnNumberRequest<TSelf> request, | ||
CancellationToken cancellationToken) | ||
{ | ||
return new ValueTask<TSelf>(request.Value); | ||
} | ||
} | ||
|
||
public class AddOnePipelineBehavior<TSelf> : IPipelineBehavior<ReturnNumberRequest<TSelf>, TSelf> | ||
where TSelf : INumber<TSelf>, IAdditionOperators<TSelf, TSelf, TSelf> | ||
{ | ||
public async ValueTask<TSelf> Handle(IServiceProvider provider, ReturnNumberRequest<TSelf> request, RequestHandlerDelegate<TSelf> next, | ||
CancellationToken cancellationToken) | ||
{ | ||
var result = await next(); | ||
|
||
return result + TSelf.One; | ||
} | ||
} | ||
|
||
public class BehaviorNumberTest | ||
{ | ||
[Fact] | ||
public async Task ReturnSelf() | ||
{ | ||
await using var provider = new ServiceCollection() | ||
.AddMediator(b => | ||
{ | ||
b.AddRequestHandler(typeof(ReturnNumberRequestHandler<>)); | ||
}) | ||
.BuildServiceProvider(); | ||
|
||
var mediator = provider.GetRequiredService<IMediator>(); | ||
|
||
Assert.Equal(0, await mediator.Send(new ReturnNumberRequest<int>(0))); | ||
Assert.Equal(10L, await mediator.Send(new ReturnNumberRequest<long>(10L))); | ||
} | ||
|
||
[Fact] | ||
public async Task AddOneBehavior() | ||
{ | ||
await using var provider = new ServiceCollection() | ||
.AddMediator(b => | ||
{ | ||
b.AddRequestHandler(typeof(ReturnNumberRequestHandler<>)); | ||
b.AddPipelineBehavior(typeof(AddOnePipelineBehavior<>)); | ||
}) | ||
.BuildServiceProvider(); | ||
|
||
var mediator = provider.GetRequiredService<IMediator>(); | ||
|
||
Assert.Equal(1, await mediator.Send(new ReturnNumberRequest<int>(0))); | ||
Assert.Equal(11L, await mediator.Send(new ReturnNumberRequest<long>(10L))); | ||
} | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters