Compare commits
2 Commits
54ea0925dd
...
7c3dd84971
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c3dd84971 | |||
| d437d06c09 |
38
.gitea/workflows/publish-nuget.yaml
Normal file
38
.gitea/workflows/publish-nuget.yaml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
name: .NET Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||||
|
- 'v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup .NET
|
||||||
|
uses: https://github.com/actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: 9.x
|
||||||
|
|
||||||
|
- name: Restore dependencies
|
||||||
|
run: dotnet restore --nologo
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: dotnet test --nologo --no-restore --configuration Release
|
||||||
|
|
||||||
|
- name: Create the package
|
||||||
|
env:
|
||||||
|
RELEASE_VERSION: ${{ gitea.ref_name }}
|
||||||
|
run: >
|
||||||
|
dotnet pack --no-restore --configuration Release --output nupkgs
|
||||||
|
`echo $RELEASE_VERSION | sed -E 's|^(v([0-9]+(\.[0-9]+){2}))(-([a-z0-9]+)){1}|/p:ReleaseVersion=\2 /p:VersionSuffix=\5|; s|^(v([0-9]+(\.[0-9]+){2}))$|/p:ReleaseVersion=\2|'`
|
||||||
|
|
||||||
|
- name: Publish the package to Gitea
|
||||||
|
run: dotnet nuget push --source ${{ vars.OUTPUT_NUGET_REGISTRY }} --api-key ${{ secrets.LOCAL_NUGET_PACKAGE_TOKEN }} nupkgs/*.nupkg
|
||||||
|
|
||||||
|
- name: Publish the package to NuGet.org
|
||||||
|
run: dotnet nuget push --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_PACKAGE_TOKEN }} nupkgs/*.nupkg
|
||||||
@@ -7,11 +7,22 @@
|
|||||||
<Description>Lightweight, easy-to-use C# library designed to simplify the implementation of the Command Query Responsibility Segregation (CQRS) pattern.</Description>
|
<Description>Lightweight, easy-to-use C# library designed to simplify the implementation of the Command Query Responsibility Segregation (CQRS) pattern.</Description>
|
||||||
<Authors>JustFixMe</Authors>
|
<Authors>JustFixMe</Authors>
|
||||||
<Copyright>Copyright (c) 2025 JustFixMe</Copyright>
|
<Copyright>Copyright (c) 2025 JustFixMe</Copyright>
|
||||||
<RepositoryUrl>https://github.com/JustFixMe/Just.Core/</RepositoryUrl>
|
<RepositoryUrl>https://github.com/JustFixMe/Just.Cqrs/</RepositoryUrl>
|
||||||
|
|
||||||
<PackageTags>c#;CQRS</PackageTags>
|
<PackageTags>csharp;cqrs;cqrs-pattern;</PackageTags>
|
||||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
<PackageReadmeFile>readme.md</PackageReadmeFile>
|
<PackageReadmeFile>readme.md</PackageReadmeFile>
|
||||||
|
|
||||||
|
<ReleaseVersion Condition=" '$(ReleaseVersion)' == '' ">1.0.0</ReleaseVersion>
|
||||||
|
<VersionSuffix Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</VersionSuffix>
|
||||||
|
<VersionPrefix Condition=" '$(VersionSuffix)' != '' ">$(ReleaseVersion)</VersionPrefix>
|
||||||
|
<Version Condition=" '$(VersionSuffix)' == '' ">$(ReleaseVersion)</Version>
|
||||||
|
<AssemblyVersion>$(ReleaseVersion)</AssemblyVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\..\readme.md" Pack="true" PackagePath=""/>
|
||||||
|
<None Include="..\..\LICENSE" Pack="true" PackagePath=""/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
76
readme.md
76
readme.md
@@ -7,24 +7,90 @@ Inspired by [MediatR](https://github.com/jbogard/MediatR)
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Separate dispatching of Commands/Queries
|
* Separate dispatching of Commands/Queries
|
||||||
* Middleware-like behaviours
|
* Middleware-like Behaviors
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
**Just.Cqrs** is built for .Net Standard 2.1 and .NET 8.0 and 9.0.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
### Install from NuGet.org
|
### Install from NuGet.org
|
||||||
|
|
||||||
```
|
```bash
|
||||||
# install the package using NuGet
|
# install the package using NuGet
|
||||||
dotnet add package Just.Cqrs
|
dotnet add package Just.Cqrs
|
||||||
```
|
```
|
||||||
|
|
||||||
### Register in DI with ```IServiceCollection```
|
### Register in DI with ```IServiceCollection```
|
||||||
|
|
||||||
```cs
|
```csharp
|
||||||
services.AddCqrs(opt => opt
|
services.AddCqrs(opt => opt
|
||||||
.AddQueryHandler<SomeQueryHandler>()
|
.AddQueryHandler<SomeQueryHandler>()
|
||||||
.AddCommandHandler<SomeCommandHandler>()
|
.AddCommandHandler<SomeCommandHandler>()
|
||||||
.AddBehaviour<SomeBehaviour>()
|
.AddBehavior<SomeQueryBehavior>()
|
||||||
.AddOpenBehaviour(typeof(SomeOpenBehaviour<,>))
|
.AddOpenBehavior(typeof(LoggingBehavior<,>))
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
### Define a Query and Handler
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
record GetUserByIdQuery(int UserId) : IKnownQuery<User>;
|
||||||
|
|
||||||
|
class GetUserByIdQueryHandler : IQueryHandler<GetUserByIdQuery, User>
|
||||||
|
{
|
||||||
|
public ValueTask<User> Handle(GetUserByIdQuery query, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// Fetch user logic here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use Dispatcher to execute the query
|
||||||
|
class GetUserByIdUseCase(IQueryDispatcher dispatcher)
|
||||||
|
{
|
||||||
|
public async Task<IResult> Execute(int userId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var user = await dispatcher.Dispatch(new GetUserByIdQuery(userId), cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
\* *the same principles apply to commands*
|
||||||
|
|
||||||
|
### Define a Behavior
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class LoggingBehavior<TRequest, TResult>(ILogger logger) : IDispatchBehavior<TRequest, TResult>
|
||||||
|
where TRequest: notnull
|
||||||
|
{
|
||||||
|
public async ValueTask<TResult> Handle(
|
||||||
|
TRequest request,
|
||||||
|
DispatchFurtherDelegate<TResult> next,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Handling request: {RequestType}", typeof(TRequest).Name);
|
||||||
|
var result = await next();
|
||||||
|
logger.LogInformation("Request handled: {RequestType}", typeof(TRequest).Name);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SomeQueryBehavior : IDispatchBehavior<SomeQuery, SomeQueryResult>
|
||||||
|
{
|
||||||
|
public async ValueTask<SomeQueryResult> Handle(
|
||||||
|
SomeQuery request,
|
||||||
|
DispatchFurtherDelegate<SomeQueryResult> next,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// do something
|
||||||
|
return await next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
**Just.Cqrs** is licensed under the [MIT License](LICENSE).
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public delegate ValueTask<TResponse> DispatchFurtherDelegate<TResponse>();
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marker interface for static type checking. Should not be used directly.
|
/// Marker interface for static type checking. Should not be used directly.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IDispatchBehaviour
|
public interface IDispatchBehavior
|
||||||
{
|
{
|
||||||
Type RequestType { get; }
|
Type RequestType { get; }
|
||||||
Type ResponseType { get; }
|
Type ResponseType { get; }
|
||||||
@@ -23,7 +23,7 @@ public interface IDispatchBehaviour
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TRequest">Request type</typeparam>
|
/// <typeparam name="TRequest">Request type</typeparam>
|
||||||
/// <typeparam name="TResponse">Result type of dispatching command/query</typeparam>
|
/// <typeparam name="TResponse">Result type of dispatching command/query</typeparam>
|
||||||
public interface IDispatchBehaviour<in TRequest, TResponse> : IDispatchBehaviour
|
public interface IDispatchBehavior<in TRequest, TResponse> : IDispatchBehavior
|
||||||
where TRequest : notnull
|
where TRequest : notnull
|
||||||
{
|
{
|
||||||
ValueTask<TResponse> Handle(
|
ValueTask<TResponse> Handle(
|
||||||
@@ -32,7 +32,7 @@ public interface IDispatchBehaviour<in TRequest, TResponse> : IDispatchBehaviour
|
|||||||
CancellationToken cancellationToken);
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
Type IDispatchBehaviour.RequestType => typeof(TRequest);
|
Type IDispatchBehavior.RequestType => typeof(TRequest);
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
Type IDispatchBehaviour.ResponseType => typeof(TResponse);
|
Type IDispatchBehavior.ResponseType => typeof(TResponse);
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ public static class CqrsServicesExtensions
|
|||||||
{
|
{
|
||||||
services.TryAdd(new ServiceDescriptor(service, impl, lifetime));
|
services.TryAdd(new ServiceDescriptor(service, impl, lifetime));
|
||||||
}
|
}
|
||||||
foreach (var (service, impl, lifetime) in options.Behaviours)
|
foreach (var (service, impl, lifetime) in options.Behaviors)
|
||||||
{
|
{
|
||||||
services.Add(new ServiceDescriptor(service, impl, lifetime));
|
services.Add(new ServiceDescriptor(service, impl, lifetime));
|
||||||
}
|
}
|
||||||
@@ -75,43 +75,43 @@ public static class CqrsServicesExtensions
|
|||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CqrsServicesOptions AddOpenBehaviour(this CqrsServicesOptions options, Type behaviour, ServiceLifetime lifetime = ServiceLifetime.Singleton)
|
public static CqrsServicesOptions AddOpenBehavior(this CqrsServicesOptions options, Type Behavior, ServiceLifetime lifetime = ServiceLifetime.Singleton)
|
||||||
{
|
{
|
||||||
var interfaces = behaviour.FindInterfaces(
|
var interfaces = Behavior.FindInterfaces(
|
||||||
static (x, t) => x.IsGenericType && x.GetGenericTypeDefinition() == (Type)t!,
|
static (x, t) => x.IsGenericType && x.GetGenericTypeDefinition() == (Type)t!,
|
||||||
typeof(IDispatchBehaviour<,>));
|
typeof(IDispatchBehavior<,>));
|
||||||
|
|
||||||
if (interfaces.Length == 0)
|
if (interfaces.Length == 0)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Supplied type does not implement IDispatchBehaviour<,> interface.", nameof(behaviour));
|
throw new ArgumentException("Supplied type does not implement IDispatchBehavior<,> interface.", nameof(Behavior));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!behaviour.ContainsGenericParameters)
|
if (!Behavior.ContainsGenericParameters)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Supplied type is not sutable for open behaviour.", nameof(behaviour));
|
throw new ArgumentException("Supplied type is not sutable for open Behavior.", nameof(Behavior));
|
||||||
}
|
}
|
||||||
|
|
||||||
options.Behaviours.Add((typeof(IDispatchBehaviour<,>), behaviour, lifetime));
|
options.Behaviors.Add((typeof(IDispatchBehavior<,>), Behavior, lifetime));
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CqrsServicesOptions AddBehaviour<TBehaviour>(this CqrsServicesOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton)
|
public static CqrsServicesOptions AddBehavior<TBehavior>(this CqrsServicesOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton)
|
||||||
where TBehaviour : notnull, IDispatchBehaviour
|
where TBehavior : notnull, IDispatchBehavior
|
||||||
{
|
{
|
||||||
var type = typeof(TBehaviour);
|
var type = typeof(TBehavior);
|
||||||
|
|
||||||
var interfaces = type.FindInterfaces(
|
var interfaces = type.FindInterfaces(
|
||||||
static (x, t) => x.IsGenericType && x.GetGenericTypeDefinition() == (Type)t!,
|
static (x, t) => x.IsGenericType && x.GetGenericTypeDefinition() == (Type)t!,
|
||||||
typeof(IDispatchBehaviour<,>));
|
typeof(IDispatchBehavior<,>));
|
||||||
|
|
||||||
if (interfaces.Length == 0)
|
if (interfaces.Length == 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Supplied type does not implement IDispatchBehaviour<,> interface.");
|
throw new InvalidOperationException("Supplied type does not implement IDispatchBehavior<,> interface.");
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var interfaceType in interfaces)
|
foreach (var interfaceType in interfaces)
|
||||||
{
|
{
|
||||||
options.Behaviours.Add((
|
options.Behaviors.Add((
|
||||||
interfaceType,
|
interfaceType,
|
||||||
type,
|
type,
|
||||||
lifetime));
|
lifetime));
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace Just.Cqrs;
|
|||||||
|
|
||||||
public sealed class CqrsServicesOptions(IServiceCollection services)
|
public sealed class CqrsServicesOptions(IServiceCollection services)
|
||||||
{
|
{
|
||||||
internal readonly List<(Type Service, Type Impl, ServiceLifetime Lifetime)> Behaviours = [];
|
internal readonly List<(Type Service, Type Impl, ServiceLifetime Lifetime)> Behaviors = [];
|
||||||
internal readonly List<(Type Service, Type Impl, ServiceLifetime Lifetime)> CommandHandlers = [];
|
internal readonly List<(Type Service, Type Impl, ServiceLifetime Lifetime)> CommandHandlers = [];
|
||||||
internal readonly List<(Type Service, Type Impl, ServiceLifetime Lifetime)> QueryHandlers = [];
|
internal readonly List<(Type Service, Type Impl, ServiceLifetime Lifetime)> QueryHandlers = [];
|
||||||
|
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ internal sealed class CommandDispatcherImpl(
|
|||||||
where TCommand : notnull
|
where TCommand : notnull
|
||||||
{
|
{
|
||||||
var handler = services.GetRequiredService<ICommandHandler<TCommand, TCommandResult>>();
|
var handler = services.GetRequiredService<ICommandHandler<TCommand, TCommandResult>>();
|
||||||
var pipeline = services.GetServices<IDispatchBehaviour<TCommand, TCommandResult>>();
|
var pipeline = services.GetServices<IDispatchBehavior<TCommand, TCommandResult>>();
|
||||||
using var pipelineEnumerator = pipeline.GetEnumerator();
|
using var pipelineEnumerator = pipeline.GetEnumerator();
|
||||||
|
|
||||||
return DispatchDelegateFactory(pipelineEnumerator).Invoke();
|
return DispatchDelegateFactory(pipelineEnumerator).Invoke();
|
||||||
|
|
||||||
DispatchFurtherDelegate<TCommandResult> DispatchDelegateFactory(IEnumerator<IDispatchBehaviour<TCommand, TCommandResult>> enumerator) =>
|
DispatchFurtherDelegate<TCommandResult> DispatchDelegateFactory(IEnumerator<IDispatchBehavior<TCommand, TCommandResult>> enumerator) =>
|
||||||
enumerator.MoveNext()
|
enumerator.MoveNext()
|
||||||
? (() => enumerator.Current.Handle(command, DispatchDelegateFactory(enumerator), cancellationToken))
|
? (() => enumerator.Current.Handle(command, DispatchDelegateFactory(enumerator), cancellationToken))
|
||||||
: (() => handler.Handle(command, cancellationToken));
|
: (() => handler.Handle(command, cancellationToken));
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ internal sealed class QueryDispatcherImpl(
|
|||||||
where TQuery : notnull
|
where TQuery : notnull
|
||||||
{
|
{
|
||||||
var handler = services.GetRequiredService<IQueryHandler<TQuery, TQueryResult>>();
|
var handler = services.GetRequiredService<IQueryHandler<TQuery, TQueryResult>>();
|
||||||
var pipeline = services.GetServices<IDispatchBehaviour<TQuery, TQueryResult>>();
|
var pipeline = services.GetServices<IDispatchBehavior<TQuery, TQueryResult>>();
|
||||||
using var pipelineEnumerator = pipeline.GetEnumerator();
|
using var pipelineEnumerator = pipeline.GetEnumerator();
|
||||||
|
|
||||||
return DispatchDelegateFactory(pipelineEnumerator).Invoke();
|
return DispatchDelegateFactory(pipelineEnumerator).Invoke();
|
||||||
|
|
||||||
DispatchFurtherDelegate<TQueryResult> DispatchDelegateFactory(IEnumerator<IDispatchBehaviour<TQuery, TQueryResult>> enumerator) =>
|
DispatchFurtherDelegate<TQueryResult> DispatchDelegateFactory(IEnumerator<IDispatchBehavior<TQuery, TQueryResult>> enumerator) =>
|
||||||
enumerator.MoveNext()
|
enumerator.MoveNext()
|
||||||
? (() => enumerator.Current.Handle(query, DispatchDelegateFactory(enumerator), cancellationToken))
|
? (() => enumerator.Current.Handle(query, DispatchDelegateFactory(enumerator), cancellationToken))
|
||||||
: (() => handler.Handle(query, cancellationToken));
|
: (() => handler.Handle(query, cancellationToken));
|
||||||
|
|||||||
@@ -42,12 +42,12 @@ public class Dispatch
|
|||||||
await commandHandler.Received(1).Handle(testCommand, CancellationToken.None);
|
await commandHandler.Received(1).Handle(testCommand, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TestOpenBehaviour<TRequest, TResponse> : IDispatchBehaviour<TRequest, TResponse>
|
public class TestOpenBehavior<TRequest, TResponse> : IDispatchBehavior<TRequest, TResponse>
|
||||||
where TRequest : notnull
|
where TRequest : notnull
|
||||||
{
|
{
|
||||||
private readonly Action<TRequest> _callback;
|
private readonly Action<TRequest> _callback;
|
||||||
|
|
||||||
public TestOpenBehaviour(Action<TRequest> callback)
|
public TestOpenBehavior(Action<TRequest> callback)
|
||||||
{
|
{
|
||||||
_callback = callback;
|
_callback = callback;
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ public class Dispatch
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task WhenPipelineConfigured_ShouldCallAllBehavioursInOrder()
|
public async Task WhenPipelineConfigured_ShouldCallAllBehaviorsInOrder()
|
||||||
{
|
{
|
||||||
// Given
|
// Given
|
||||||
var testCommand = new TestCommand();
|
var testCommand = new TestCommand();
|
||||||
@@ -72,15 +72,15 @@ public class Dispatch
|
|||||||
.Returns(testCommandResult)
|
.Returns(testCommandResult)
|
||||||
.AndDoes(_ => calls.Add("commandHandler"));
|
.AndDoes(_ => calls.Add("commandHandler"));
|
||||||
|
|
||||||
var firstBehaviour = Substitute.For<IDispatchBehaviour<TestCommand, TestCommandResult>>();
|
var firstBehavior = Substitute.For<IDispatchBehavior<TestCommand, TestCommandResult>>();
|
||||||
firstBehaviour.Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>())
|
firstBehavior.Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>())
|
||||||
.Returns(args => ((DispatchFurtherDelegate<TestCommandResult>)args[1]).Invoke())
|
.Returns(args => ((DispatchFurtherDelegate<TestCommandResult>)args[1]).Invoke())
|
||||||
.AndDoes(_ => calls.Add("firstBehaviour"));
|
.AndDoes(_ => calls.Add("firstBehavior"));
|
||||||
|
|
||||||
var secondBehaviour = Substitute.For<IDispatchBehaviour<TestCommand, TestCommandResult>>();
|
var secondBehavior = Substitute.For<IDispatchBehavior<TestCommand, TestCommandResult>>();
|
||||||
secondBehaviour.Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>())
|
secondBehavior.Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>())
|
||||||
.Returns(args => ((DispatchFurtherDelegate<TestCommandResult>)args[1]).Invoke())
|
.Returns(args => ((DispatchFurtherDelegate<TestCommandResult>)args[1]).Invoke())
|
||||||
.AndDoes(_ => calls.Add("secondBehaviour"));
|
.AndDoes(_ => calls.Add("secondBehavior"));
|
||||||
|
|
||||||
ServiceCollection serviceCollection =
|
ServiceCollection serviceCollection =
|
||||||
[
|
[
|
||||||
@@ -90,22 +90,22 @@ public class Dispatch
|
|||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
new ServiceDescriptor(
|
new ServiceDescriptor(
|
||||||
typeof(IDispatchBehaviour<TestCommand, TestCommandResult>),
|
typeof(IDispatchBehavior<TestCommand, TestCommandResult>),
|
||||||
(IServiceProvider _) => firstBehaviour,
|
(IServiceProvider _) => firstBehavior,
|
||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
new ServiceDescriptor(
|
new ServiceDescriptor(
|
||||||
typeof(IDispatchBehaviour<TestCommand, TestCommandResult>),
|
typeof(IDispatchBehavior<TestCommand, TestCommandResult>),
|
||||||
(IServiceProvider _) => secondBehaviour,
|
(IServiceProvider _) => secondBehavior,
|
||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
new ServiceDescriptor(
|
new ServiceDescriptor(
|
||||||
typeof(IDispatchBehaviour<,>),
|
typeof(IDispatchBehavior<,>),
|
||||||
typeof(TestOpenBehaviour<,>),
|
typeof(TestOpenBehavior<,>),
|
||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
serviceCollection.AddTransient<Action<TestCommand>>(_ => (TestCommand _) => calls.Add("thirdBehaviour"));
|
serviceCollection.AddTransient<Action<TestCommand>>(_ => (TestCommand _) => calls.Add("thirdBehavior"));
|
||||||
var services = serviceCollection.BuildServiceProvider();
|
var services = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
var sut = new CommandDispatcherImpl(services, new ConcurrentMethodsCache());
|
var sut = new CommandDispatcherImpl(services, new ConcurrentMethodsCache());
|
||||||
@@ -115,11 +115,11 @@ public class Dispatch
|
|||||||
|
|
||||||
// Then
|
// Then
|
||||||
result.ShouldBeSameAs(testCommandResult);
|
result.ShouldBeSameAs(testCommandResult);
|
||||||
await firstBehaviour.Received(1).Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>());
|
await firstBehavior.Received(1).Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>());
|
||||||
await secondBehaviour.Received(1).Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>());
|
await secondBehavior.Received(1).Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>());
|
||||||
await commandHandler.Received(1).Handle(testCommand, CancellationToken.None);
|
await commandHandler.Received(1).Handle(testCommand, CancellationToken.None);
|
||||||
|
|
||||||
calls.ShouldBe(["firstBehaviour", "secondBehaviour", "thirdBehaviour", "commandHandler"]);
|
calls.ShouldBe(["firstBehavior", "secondBehavior", "thirdBehavior", "commandHandler"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -136,15 +136,15 @@ public class Dispatch
|
|||||||
.Returns(testCommandResult)
|
.Returns(testCommandResult)
|
||||||
.AndDoes(_ => calls.Add("commandHandler"));
|
.AndDoes(_ => calls.Add("commandHandler"));
|
||||||
|
|
||||||
var firstBehaviour = Substitute.For<IDispatchBehaviour<TestCommand, TestCommandResult>>();
|
var firstBehavior = Substitute.For<IDispatchBehavior<TestCommand, TestCommandResult>>();
|
||||||
firstBehaviour.Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>())
|
firstBehavior.Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>())
|
||||||
.Returns(args => ((DispatchFurtherDelegate<TestCommandResult>)args[1]).Invoke())
|
.Returns(args => ((DispatchFurtherDelegate<TestCommandResult>)args[1]).Invoke())
|
||||||
.AndDoes(_ => calls.Add("firstBehaviour"));
|
.AndDoes(_ => calls.Add("firstBehavior"));
|
||||||
|
|
||||||
var secondBehaviour = Substitute.For<IDispatchBehaviour<TestCommand, TestCommandResult>>();
|
var secondBehavior = Substitute.For<IDispatchBehavior<TestCommand, TestCommandResult>>();
|
||||||
secondBehaviour.Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>())
|
secondBehavior.Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>())
|
||||||
.Returns(args => ValueTask.FromResult(testCommandResultAborted))
|
.Returns(args => ValueTask.FromResult(testCommandResultAborted))
|
||||||
.AndDoes(_ => calls.Add("secondBehaviour"));
|
.AndDoes(_ => calls.Add("secondBehavior"));
|
||||||
|
|
||||||
ServiceCollection serviceCollection =
|
ServiceCollection serviceCollection =
|
||||||
[
|
[
|
||||||
@@ -154,22 +154,22 @@ public class Dispatch
|
|||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
new ServiceDescriptor(
|
new ServiceDescriptor(
|
||||||
typeof(IDispatchBehaviour<TestCommand, TestCommandResult>),
|
typeof(IDispatchBehavior<TestCommand, TestCommandResult>),
|
||||||
(IServiceProvider _) => firstBehaviour,
|
(IServiceProvider _) => firstBehavior,
|
||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
new ServiceDescriptor(
|
new ServiceDescriptor(
|
||||||
typeof(IDispatchBehaviour<TestCommand, TestCommandResult>),
|
typeof(IDispatchBehavior<TestCommand, TestCommandResult>),
|
||||||
(IServiceProvider _) => secondBehaviour,
|
(IServiceProvider _) => secondBehavior,
|
||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
new ServiceDescriptor(
|
new ServiceDescriptor(
|
||||||
typeof(IDispatchBehaviour<,>),
|
typeof(IDispatchBehavior<,>),
|
||||||
typeof(TestOpenBehaviour<,>),
|
typeof(TestOpenBehavior<,>),
|
||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
serviceCollection.AddTransient<Action<TestCommand>>(_ => (TestCommand _) => calls.Add("thirdBehaviour"));
|
serviceCollection.AddTransient<Action<TestCommand>>(_ => (TestCommand _) => calls.Add("thirdBehavior"));
|
||||||
var services = serviceCollection.BuildServiceProvider();
|
var services = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
var sut = new CommandDispatcherImpl(services, new ConcurrentMethodsCache());
|
var sut = new CommandDispatcherImpl(services, new ConcurrentMethodsCache());
|
||||||
@@ -179,10 +179,10 @@ public class Dispatch
|
|||||||
|
|
||||||
// Then
|
// Then
|
||||||
result.ShouldBeSameAs(testCommandResultAborted);
|
result.ShouldBeSameAs(testCommandResultAborted);
|
||||||
await firstBehaviour.Received(1).Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>());
|
await firstBehavior.Received(1).Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>());
|
||||||
await secondBehaviour.Received(1).Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>());
|
await secondBehavior.Received(1).Handle(testCommand, Arg.Any<DispatchFurtherDelegate<TestCommandResult>>(), Arg.Any<CancellationToken>());
|
||||||
await commandHandler.Received(0).Handle(testCommand, CancellationToken.None);
|
await commandHandler.Received(0).Handle(testCommand, CancellationToken.None);
|
||||||
|
|
||||||
calls.ShouldBe(["firstBehaviour", "secondBehaviour"]);
|
calls.ShouldBe(["firstBehavior", "secondBehavior"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
|
|
||||||
namespace Cqrs.Tests.CqrsServicesExtensionsTests;
|
namespace Cqrs.Tests.CqrsServicesExtensionsTests;
|
||||||
|
|
||||||
public class AddBehaviour
|
public class AddBehavior
|
||||||
{
|
{
|
||||||
public class TestCommand {}
|
public class TestCommand {}
|
||||||
public class TestCommandResult {}
|
public class TestCommandResult {}
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
public class NonGenericTestOpenBehaviour : IDispatchBehaviour<TestCommand, TestCommandResult>
|
public class NonGenericTestOpenBehavior : IDispatchBehavior<TestCommand, TestCommandResult>
|
||||||
{
|
{
|
||||||
public ValueTask<TestCommandResult> Handle(TestCommand request, DispatchFurtherDelegate<TestCommandResult> next, CancellationToken cancellationToken)
|
public ValueTask<TestCommandResult> Handle(TestCommand request, DispatchFurtherDelegate<TestCommandResult> next, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -20,27 +20,27 @@ public class AddBehaviour
|
|||||||
[InlineData(ServiceLifetime.Transient)]
|
[InlineData(ServiceLifetime.Transient)]
|
||||||
[InlineData(ServiceLifetime.Scoped)]
|
[InlineData(ServiceLifetime.Scoped)]
|
||||||
[InlineData(ServiceLifetime.Singleton)]
|
[InlineData(ServiceLifetime.Singleton)]
|
||||||
public void WhenCalled_ShouldRegisterDispatchBehaviour(ServiceLifetime lifetime)
|
public void WhenCalled_ShouldRegisterDispatchBehavior(ServiceLifetime lifetime)
|
||||||
{
|
{
|
||||||
// Given
|
// Given
|
||||||
ServiceCollection services = new();
|
ServiceCollection services = new();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
services.AddCqrs(opt => opt
|
services.AddCqrs(opt => opt
|
||||||
.AddBehaviour<NonGenericTestOpenBehaviour>(lifetime));
|
.AddBehavior<NonGenericTestOpenBehavior>(lifetime));
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
services.ShouldContain(
|
services.ShouldContain(
|
||||||
elementPredicate: descriptor =>
|
elementPredicate: descriptor =>
|
||||||
descriptor.ServiceType == typeof(IDispatchBehaviour<TestCommand, TestCommandResult>)
|
descriptor.ServiceType == typeof(IDispatchBehavior<TestCommand, TestCommandResult>)
|
||||||
&& descriptor.ImplementationType == typeof(NonGenericTestOpenBehaviour)
|
&& descriptor.ImplementationType == typeof(NonGenericTestOpenBehavior)
|
||||||
&& descriptor.Lifetime == lifetime,
|
&& descriptor.Lifetime == lifetime,
|
||||||
expectedCount: 1
|
expectedCount: 1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
public class InvalidTestBehaviour : IDispatchBehaviour
|
public class InvalidTestBehavior : IDispatchBehavior
|
||||||
{
|
{
|
||||||
public Type RequestType => throw new NotImplementedException();
|
public Type RequestType => throw new NotImplementedException();
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ public class AddBehaviour
|
|||||||
|
|
||||||
// Then
|
// Then
|
||||||
Should.Throw<InvalidOperationException>(() => services.AddCqrs(opt => opt
|
Should.Throw<InvalidOperationException>(() => services.AddCqrs(opt => opt
|
||||||
.AddBehaviour<InvalidTestBehaviour>())
|
.AddBehavior<InvalidTestBehavior>())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,10 +3,10 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
|
|
||||||
namespace Cqrs.Tests.CqrsServicesExtensionsTests;
|
namespace Cqrs.Tests.CqrsServicesExtensionsTests;
|
||||||
|
|
||||||
public class AddOpenBehaviour
|
public class AddOpenBehavior
|
||||||
{
|
{
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
public class TestOpenBehaviour<TRequest, TResponse> : IDispatchBehaviour<TRequest, TResponse>
|
public class TestOpenBehavior<TRequest, TResponse> : IDispatchBehavior<TRequest, TResponse>
|
||||||
where TRequest: notnull
|
where TRequest: notnull
|
||||||
{
|
{
|
||||||
public ValueTask<TResponse> Handle(TRequest request, DispatchFurtherDelegate<TResponse> next, CancellationToken cancellationToken)
|
public ValueTask<TResponse> Handle(TRequest request, DispatchFurtherDelegate<TResponse> next, CancellationToken cancellationToken)
|
||||||
@@ -19,27 +19,27 @@ public class AddOpenBehaviour
|
|||||||
[InlineData(ServiceLifetime.Transient)]
|
[InlineData(ServiceLifetime.Transient)]
|
||||||
[InlineData(ServiceLifetime.Scoped)]
|
[InlineData(ServiceLifetime.Scoped)]
|
||||||
[InlineData(ServiceLifetime.Singleton)]
|
[InlineData(ServiceLifetime.Singleton)]
|
||||||
public void WhenCalled_ShouldRegisterOpenDispatchBehaviour(ServiceLifetime lifetime)
|
public void WhenCalled_ShouldRegisterOpenDispatchBehavior(ServiceLifetime lifetime)
|
||||||
{
|
{
|
||||||
// Given
|
// Given
|
||||||
ServiceCollection services = new();
|
ServiceCollection services = new();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
services.AddCqrs(opt => opt
|
services.AddCqrs(opt => opt
|
||||||
.AddOpenBehaviour(typeof(TestOpenBehaviour<,>), lifetime));
|
.AddOpenBehavior(typeof(TestOpenBehavior<,>), lifetime));
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
services.ShouldContain(
|
services.ShouldContain(
|
||||||
elementPredicate: descriptor =>
|
elementPredicate: descriptor =>
|
||||||
descriptor.ServiceType == typeof(IDispatchBehaviour<,>)
|
descriptor.ServiceType == typeof(IDispatchBehavior<,>)
|
||||||
&& descriptor.ImplementationType == typeof(TestOpenBehaviour<,>)
|
&& descriptor.ImplementationType == typeof(TestOpenBehavior<,>)
|
||||||
&& descriptor.Lifetime == lifetime,
|
&& descriptor.Lifetime == lifetime,
|
||||||
expectedCount: 1
|
expectedCount: 1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
public class InvalidOpenBehaviour : IDispatchBehaviour
|
public class InvalidOpenBehavior : IDispatchBehavior
|
||||||
{
|
{
|
||||||
public Type RequestType => throw new NotImplementedException();
|
public Type RequestType => throw new NotImplementedException();
|
||||||
|
|
||||||
@@ -53,18 +53,18 @@ public class AddOpenBehaviour
|
|||||||
ServiceCollection services = new();
|
ServiceCollection services = new();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
var invalidOpenDispatchBehaviourType = typeof(InvalidOpenBehaviour);
|
var invalidOpenDispatchBehaviorType = typeof(InvalidOpenBehavior);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
Should.Throw<ArgumentException>(() => services.AddCqrs(opt => opt
|
Should.Throw<ArgumentException>(() => services.AddCqrs(opt => opt
|
||||||
.AddOpenBehaviour(invalidOpenDispatchBehaviourType))
|
.AddOpenBehavior(invalidOpenDispatchBehaviorType))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TestCommand {}
|
public class TestCommand {}
|
||||||
public class TestCommandResult {}
|
public class TestCommandResult {}
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
public class NonGenericTestOpenBehaviour : IDispatchBehaviour<TestCommand, TestCommandResult>
|
public class NonGenericTestOpenBehavior : IDispatchBehavior<TestCommand, TestCommandResult>
|
||||||
{
|
{
|
||||||
public ValueTask<TestCommandResult> Handle(TestCommand request, DispatchFurtherDelegate<TestCommandResult> next, CancellationToken cancellationToken)
|
public ValueTask<TestCommandResult> Handle(TestCommand request, DispatchFurtherDelegate<TestCommandResult> next, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -79,11 +79,11 @@ public class AddOpenBehaviour
|
|||||||
ServiceCollection services = new();
|
ServiceCollection services = new();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
var nonGenericOpenDispatchBehaviourType = typeof(NonGenericTestOpenBehaviour);
|
var nonGenericOpenDispatchBehaviorType = typeof(NonGenericTestOpenBehavior);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
Should.Throw<ArgumentException>(() => services.AddCqrs(opt => opt
|
Should.Throw<ArgumentException>(() => services.AddCqrs(opt => opt
|
||||||
.AddOpenBehaviour(nonGenericOpenDispatchBehaviourType))
|
.AddOpenBehavior(nonGenericOpenDispatchBehaviorType))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,12 +42,12 @@ public class Dispatch
|
|||||||
await queryHandler.Received(1).Handle(testQuery, CancellationToken.None);
|
await queryHandler.Received(1).Handle(testQuery, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TestOpenBehaviour<TRequest, TResponse> : IDispatchBehaviour<TRequest, TResponse>
|
public class TestOpenBehavior<TRequest, TResponse> : IDispatchBehavior<TRequest, TResponse>
|
||||||
where TRequest : notnull
|
where TRequest : notnull
|
||||||
{
|
{
|
||||||
private readonly Action<TRequest> _callback;
|
private readonly Action<TRequest> _callback;
|
||||||
|
|
||||||
public TestOpenBehaviour(Action<TRequest> callback)
|
public TestOpenBehavior(Action<TRequest> callback)
|
||||||
{
|
{
|
||||||
_callback = callback;
|
_callback = callback;
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ public class Dispatch
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task WhenPipelineConfigured_ShouldCallAllBehavioursInOrder()
|
public async Task WhenPipelineConfigured_ShouldCallAllBehaviorsInOrder()
|
||||||
{
|
{
|
||||||
// Given
|
// Given
|
||||||
var testQuery = new TestQuery();
|
var testQuery = new TestQuery();
|
||||||
@@ -72,15 +72,15 @@ public class Dispatch
|
|||||||
.Returns(testQueryResult)
|
.Returns(testQueryResult)
|
||||||
.AndDoes(_ => calls.Add("queryHandler"));
|
.AndDoes(_ => calls.Add("queryHandler"));
|
||||||
|
|
||||||
var firstBehaviour = Substitute.For<IDispatchBehaviour<TestQuery, TestQueryResult>>();
|
var firstBehavior = Substitute.For<IDispatchBehavior<TestQuery, TestQueryResult>>();
|
||||||
firstBehaviour.Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>())
|
firstBehavior.Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>())
|
||||||
.Returns(args => ((DispatchFurtherDelegate<TestQueryResult>)args[1]).Invoke())
|
.Returns(args => ((DispatchFurtherDelegate<TestQueryResult>)args[1]).Invoke())
|
||||||
.AndDoes(_ => calls.Add("firstBehaviour"));
|
.AndDoes(_ => calls.Add("firstBehavior"));
|
||||||
|
|
||||||
var secondBehaviour = Substitute.For<IDispatchBehaviour<TestQuery, TestQueryResult>>();
|
var secondBehavior = Substitute.For<IDispatchBehavior<TestQuery, TestQueryResult>>();
|
||||||
secondBehaviour.Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>())
|
secondBehavior.Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>())
|
||||||
.Returns(args => ((DispatchFurtherDelegate<TestQueryResult>)args[1]).Invoke())
|
.Returns(args => ((DispatchFurtherDelegate<TestQueryResult>)args[1]).Invoke())
|
||||||
.AndDoes(_ => calls.Add("secondBehaviour"));
|
.AndDoes(_ => calls.Add("secondBehavior"));
|
||||||
|
|
||||||
ServiceCollection serviceCollection =
|
ServiceCollection serviceCollection =
|
||||||
[
|
[
|
||||||
@@ -90,22 +90,22 @@ public class Dispatch
|
|||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
new ServiceDescriptor(
|
new ServiceDescriptor(
|
||||||
typeof(IDispatchBehaviour<TestQuery, TestQueryResult>),
|
typeof(IDispatchBehavior<TestQuery, TestQueryResult>),
|
||||||
(IServiceProvider _) => firstBehaviour,
|
(IServiceProvider _) => firstBehavior,
|
||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
new ServiceDescriptor(
|
new ServiceDescriptor(
|
||||||
typeof(IDispatchBehaviour<TestQuery, TestQueryResult>),
|
typeof(IDispatchBehavior<TestQuery, TestQueryResult>),
|
||||||
(IServiceProvider _) => secondBehaviour,
|
(IServiceProvider _) => secondBehavior,
|
||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
new ServiceDescriptor(
|
new ServiceDescriptor(
|
||||||
typeof(IDispatchBehaviour<,>),
|
typeof(IDispatchBehavior<,>),
|
||||||
typeof(TestOpenBehaviour<,>),
|
typeof(TestOpenBehavior<,>),
|
||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
serviceCollection.AddTransient<Action<TestQuery>>(_ => (TestQuery _) => calls.Add("thirdBehaviour"));
|
serviceCollection.AddTransient<Action<TestQuery>>(_ => (TestQuery _) => calls.Add("thirdBehavior"));
|
||||||
var services = serviceCollection.BuildServiceProvider();
|
var services = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
var sut = new QueryDispatcherImpl(services, new ConcurrentMethodsCache());
|
var sut = new QueryDispatcherImpl(services, new ConcurrentMethodsCache());
|
||||||
@@ -115,11 +115,11 @@ public class Dispatch
|
|||||||
|
|
||||||
// Then
|
// Then
|
||||||
result.ShouldBeSameAs(testQueryResult);
|
result.ShouldBeSameAs(testQueryResult);
|
||||||
await firstBehaviour.Received(1).Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>());
|
await firstBehavior.Received(1).Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>());
|
||||||
await secondBehaviour.Received(1).Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>());
|
await secondBehavior.Received(1).Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>());
|
||||||
await queryHandler.Received(1).Handle(testQuery, CancellationToken.None);
|
await queryHandler.Received(1).Handle(testQuery, CancellationToken.None);
|
||||||
|
|
||||||
calls.ShouldBe(["firstBehaviour", "secondBehaviour", "thirdBehaviour", "queryHandler"]);
|
calls.ShouldBe(["firstBehavior", "secondBehavior", "thirdBehavior", "queryHandler"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -136,15 +136,15 @@ public class Dispatch
|
|||||||
.Returns(testQueryResult)
|
.Returns(testQueryResult)
|
||||||
.AndDoes(_ => calls.Add("queryHandler"));
|
.AndDoes(_ => calls.Add("queryHandler"));
|
||||||
|
|
||||||
var firstBehaviour = Substitute.For<IDispatchBehaviour<TestQuery, TestQueryResult>>();
|
var firstBehavior = Substitute.For<IDispatchBehavior<TestQuery, TestQueryResult>>();
|
||||||
firstBehaviour.Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>())
|
firstBehavior.Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>())
|
||||||
.Returns(args => ((DispatchFurtherDelegate<TestQueryResult>)args[1]).Invoke())
|
.Returns(args => ((DispatchFurtherDelegate<TestQueryResult>)args[1]).Invoke())
|
||||||
.AndDoes(_ => calls.Add("firstBehaviour"));
|
.AndDoes(_ => calls.Add("firstBehavior"));
|
||||||
|
|
||||||
var secondBehaviour = Substitute.For<IDispatchBehaviour<TestQuery, TestQueryResult>>();
|
var secondBehavior = Substitute.For<IDispatchBehavior<TestQuery, TestQueryResult>>();
|
||||||
secondBehaviour.Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>())
|
secondBehavior.Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>())
|
||||||
.Returns(args => ValueTask.FromResult(testQueryResultAborted))
|
.Returns(args => ValueTask.FromResult(testQueryResultAborted))
|
||||||
.AndDoes(_ => calls.Add("secondBehaviour"));
|
.AndDoes(_ => calls.Add("secondBehavior"));
|
||||||
|
|
||||||
ServiceCollection serviceCollection =
|
ServiceCollection serviceCollection =
|
||||||
[
|
[
|
||||||
@@ -154,22 +154,22 @@ public class Dispatch
|
|||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
new ServiceDescriptor(
|
new ServiceDescriptor(
|
||||||
typeof(IDispatchBehaviour<TestQuery, TestQueryResult>),
|
typeof(IDispatchBehavior<TestQuery, TestQueryResult>),
|
||||||
(IServiceProvider _) => firstBehaviour,
|
(IServiceProvider _) => firstBehavior,
|
||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
new ServiceDescriptor(
|
new ServiceDescriptor(
|
||||||
typeof(IDispatchBehaviour<TestQuery, TestQueryResult>),
|
typeof(IDispatchBehavior<TestQuery, TestQueryResult>),
|
||||||
(IServiceProvider _) => secondBehaviour,
|
(IServiceProvider _) => secondBehavior,
|
||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
new ServiceDescriptor(
|
new ServiceDescriptor(
|
||||||
typeof(IDispatchBehaviour<,>),
|
typeof(IDispatchBehavior<,>),
|
||||||
typeof(TestOpenBehaviour<,>),
|
typeof(TestOpenBehavior<,>),
|
||||||
ServiceLifetime.Transient
|
ServiceLifetime.Transient
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
serviceCollection.AddTransient<Action<TestQuery>>(_ => (TestQuery _) => calls.Add("thirdBehaviour"));
|
serviceCollection.AddTransient<Action<TestQuery>>(_ => (TestQuery _) => calls.Add("thirdBehavior"));
|
||||||
var services = serviceCollection.BuildServiceProvider();
|
var services = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
var sut = new QueryDispatcherImpl(services, new ConcurrentMethodsCache());
|
var sut = new QueryDispatcherImpl(services, new ConcurrentMethodsCache());
|
||||||
@@ -179,10 +179,10 @@ public class Dispatch
|
|||||||
|
|
||||||
// Then
|
// Then
|
||||||
result.ShouldBeSameAs(testQueryResultAborted);
|
result.ShouldBeSameAs(testQueryResultAborted);
|
||||||
await firstBehaviour.Received(1).Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>());
|
await firstBehavior.Received(1).Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>());
|
||||||
await secondBehaviour.Received(1).Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>());
|
await secondBehavior.Received(1).Handle(testQuery, Arg.Any<DispatchFurtherDelegate<TestQueryResult>>(), Arg.Any<CancellationToken>());
|
||||||
await queryHandler.Received(0).Handle(testQuery, CancellationToken.None);
|
await queryHandler.Received(0).Handle(testQuery, CancellationToken.None);
|
||||||
|
|
||||||
calls.ShouldBe(["firstBehaviour", "secondBehaviour"]);
|
calls.ShouldBe(["firstBehavior", "secondBehavior"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user