pipeline cache and dispatch optimizations
All checks were successful
.NET Test / test (push) Successful in 13m14s
.NET Publish / publish (push) Successful in 11m1s

This commit is contained in:
2025-02-04 20:49:05 +04:00
parent 7c3dd84971
commit 2fded2809f
13 changed files with 265 additions and 74 deletions

View File

@@ -6,6 +6,10 @@ namespace Cqrs.Tests.CommandDispatcherImplTests;
public class Dispatch
{
public abstract class TestCommandHandler : ICommandHandler<TestCommand, TestCommandResult>
{
public abstract ValueTask<TestCommandResult> Handle(TestCommand command, CancellationToken cancellation);
}
public class TestCommand : IKnownCommand<TestCommandResult> {}
public class TestCommandResult {}
@@ -19,7 +23,7 @@ public class Dispatch
var testCommand = new TestCommand();
var testCommandResult = new TestCommandResult();
var commandHandler = Substitute.For<ICommandHandler<TestCommand, TestCommandResult>>();
var commandHandler = Substitute.For<TestCommandHandler>();
commandHandler.Handle(testCommand, CancellationToken.None).Returns(testCommandResult);
ServiceCollection serviceCollection =
@@ -67,7 +71,7 @@ public class Dispatch
var testCommandResult = new TestCommandResult();
List<string> calls = [];
var commandHandler = Substitute.For<ICommandHandler<TestCommand, TestCommandResult>>();
var commandHandler = Substitute.For<TestCommandHandler>();
commandHandler.Handle(testCommand, CancellationToken.None)
.Returns(testCommandResult)
.AndDoes(_ => calls.Add("commandHandler"));
@@ -131,7 +135,7 @@ public class Dispatch
var testCommandResultAborted = new TestCommandResult();
List<string> calls = [];
var commandHandler = Substitute.For<ICommandHandler<TestCommand, TestCommandResult>>();
var commandHandler = Substitute.For<TestCommandHandler>();
commandHandler.Handle(testCommand, CancellationToken.None)
.Returns(testCommandResult)
.AndDoes(_ => calls.Add("commandHandler"));
@@ -185,4 +189,53 @@ public class Dispatch
calls.ShouldBe(["firstBehavior", "secondBehavior"]);
}
public abstract class AnotherTestCommandHandler : ICommandHandler<TestCommand, AnotherTestCommandResult>
{
public abstract ValueTask<AnotherTestCommandResult> Handle(TestCommand command, CancellationToken cancellation);
}
public class AnotherTestCommandResult {}
[Fact]
public async Task WhenTwoHandlersWithDifferentResultTypesRegisteredForOneCommandType_ShouldCorrectlyDispatchToBoth() // Fix to Cache Key Collision
{
// Given
var testCommand = new TestCommand();
var testCommandResult = new TestCommandResult();
var anotherTestCommandResult = new AnotherTestCommandResult();
var commandHandler = Substitute.For<TestCommandHandler>();
commandHandler.Handle(testCommand, CancellationToken.None).Returns(testCommandResult);
var anotherCommandHandler = Substitute.For<AnotherTestCommandHandler>();
anotherCommandHandler.Handle(testCommand, CancellationToken.None).Returns(anotherTestCommandResult);
ServiceCollection serviceCollection =
[
new ServiceDescriptor(
typeof(ICommandHandler<TestCommand, TestCommandResult>),
(IServiceProvider _) => commandHandler,
ServiceLifetime.Transient
),
new ServiceDescriptor(
typeof(ICommandHandler<TestCommand, AnotherTestCommandResult>),
(IServiceProvider _) => anotherCommandHandler,
ServiceLifetime.Transient
),
];
var services = serviceCollection.BuildServiceProvider();
var sut = new CommandDispatcherImpl(services, new ConcurrentMethodsCache());
// When
var result = await sut.Dispatch(testCommand, CancellationToken.None);
var anotherResult = await sut.Dispatch<AnotherTestCommandResult>(testCommand, CancellationToken.None);
// Then
result.ShouldBeSameAs(testCommandResult);
await commandHandler.Received(1).Handle(testCommand, CancellationToken.None);
anotherResult.ShouldBeSameAs(anotherTestCommandResult);
await anotherCommandHandler.Received(1).Handle(testCommand, CancellationToken.None);
}
}

View File

@@ -6,6 +6,10 @@ namespace Cqrs.Tests.QueryDispatcherImplTests;
public class Dispatch
{
public abstract class TestQueryHandler : IQueryHandler<TestQuery, TestQueryResult>
{
public abstract ValueTask<TestQueryResult> Handle(TestQuery query, CancellationToken cancellation);
}
public class TestQuery : IKnownQuery<TestQueryResult> {}
public class TestQueryResult {}
@@ -19,7 +23,7 @@ public class Dispatch
var testQuery = new TestQuery();
var testQueryResult = new TestQueryResult();
var queryHandler = Substitute.For<IQueryHandler<TestQuery, TestQueryResult>>();
var queryHandler = Substitute.For<TestQueryHandler>();
queryHandler.Handle(testQuery, CancellationToken.None).Returns(testQueryResult);
ServiceCollection serviceCollection =
@@ -67,7 +71,7 @@ public class Dispatch
var testQueryResult = new TestQueryResult();
List<string> calls = [];
var queryHandler = Substitute.For<IQueryHandler<TestQuery, TestQueryResult>>();
var queryHandler = Substitute.For<TestQueryHandler>();
queryHandler.Handle(testQuery, CancellationToken.None)
.Returns(testQueryResult)
.AndDoes(_ => calls.Add("queryHandler"));
@@ -131,7 +135,7 @@ public class Dispatch
var testQueryResultAborted = new TestQueryResult();
List<string> calls = [];
var queryHandler = Substitute.For<IQueryHandler<TestQuery, TestQueryResult>>();
var queryHandler = Substitute.For<TestQueryHandler>();
queryHandler.Handle(testQuery, CancellationToken.None)
.Returns(testQueryResult)
.AndDoes(_ => calls.Add("queryHandler"));
@@ -185,4 +189,53 @@ public class Dispatch
calls.ShouldBe(["firstBehavior", "secondBehavior"]);
}
public abstract class AnotherTestQueryHandler : IQueryHandler<TestQuery, AnotherTestQueryResult>
{
public abstract ValueTask<AnotherTestQueryResult> Handle(TestQuery query, CancellationToken cancellation);
}
public class AnotherTestQueryResult {}
[Fact]
public async Task WhenTwoHandlersWithDifferentResultTypesRegisteredForOneQueryType_ShouldCorrectlyDispatchToBoth() // Fix to Cache Key Collision
{
// Given
var testQuery = new TestQuery();
var testQueryResult = new TestQueryResult();
var anotherTestQueryResult = new AnotherTestQueryResult();
var queryHandler = Substitute.For<TestQueryHandler>();
queryHandler.Handle(testQuery, CancellationToken.None).Returns(testQueryResult);
var anotherQueryHandler = Substitute.For<AnotherTestQueryHandler>();
anotherQueryHandler.Handle(testQuery, CancellationToken.None).Returns(anotherTestQueryResult);
ServiceCollection serviceCollection =
[
new ServiceDescriptor(
typeof(IQueryHandler<TestQuery, TestQueryResult>),
(IServiceProvider _) => queryHandler,
ServiceLifetime.Transient
),
new ServiceDescriptor(
typeof(IQueryHandler<TestQuery, AnotherTestQueryResult>),
(IServiceProvider _) => anotherQueryHandler,
ServiceLifetime.Transient
),
];
var services = serviceCollection.BuildServiceProvider();
var sut = new QueryDispatcherImpl(services, new ConcurrentMethodsCache());
// When
var result = await sut.Dispatch(testQuery, CancellationToken.None);
var anotherResult = await sut.Dispatch<AnotherTestQueryResult>(testQuery, CancellationToken.None);
// Then
result.ShouldBeSameAs(testQueryResult);
await queryHandler.Received(1).Handle(testQuery, CancellationToken.None);
anotherResult.ShouldBeSameAs(anotherTestQueryResult);
await anotherQueryHandler.Received(1).Handle(testQuery, CancellationToken.None);
}
}