From fb7189b70ac108aa0ea1367dd239bcebc604cef5 Mon Sep 17 00:00:00 2001 From: danielmarbach Date: Fri, 25 Sep 2020 15:02:31 +0200 Subject: [PATCH 1/4] Add acceptance tests to verify custom unit of work --- ...custom_unit_of_work_with_failed_message.cs | 84 +++++++++++++++++ ...om_unit_of_work_with_successful_message.cs | 89 +++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_failed_message.cs create mode 100644 src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_successful_message.cs diff --git a/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_failed_message.cs b/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_failed_message.cs new file mode 100644 index 0000000000..2913b22156 --- /dev/null +++ b/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_failed_message.cs @@ -0,0 +1,84 @@ +namespace NServiceBus.AcceptanceTests.UnitOfWork +{ + using System; + using System.Threading.Tasks; + using AcceptanceTesting; + using EndpointTemplates; + using Microsoft.Extensions.DependencyInjection; + using NServiceBus.UnitOfWork; + using NUnit.Framework; + + public class When_using_custom_unit_of_work_with_failed_message : NServiceBusAcceptanceTest + { + [Test] + public async Task Should_execute_uow_and_provide_exception_details() + { + var context = await Scenario.Define() + .WithEndpoint(g => + { + g.DoNotFailOnErrorMessages(); + g.When(b => b.SendLocal(new MyMessage())); + }) + .Done(c => c.BeginCalled && c.EndCalled) + .Run(); + + Assert.True(context.BeginCalled, "Unit of work should have been executed"); + Assert.True(context.EndCalled, "Unit of work should have been executed"); + Assert.That(context.EndException, Is.InstanceOf().And.Message.Contain("Something went wrong"), "Exception was not provided but should have been"); + } + + public class Context : ScenarioContext + { + public bool Done { get; set; } + public bool BeginCalled { get; set; } + public bool EndCalled { get; set; } + public Exception EndException { get; set; } + } + + public class EndpointWithCustomUnitOfWork : EndpointConfigurationBuilder + { + public EndpointWithCustomUnitOfWork() + { + EndpointSetup((c, r) => + { + c.RegisterComponents(services => services.AddSingleton()); + }); + } + + class CustomUnitOfWork : IManageUnitsOfWork + { + public CustomUnitOfWork(Context testContext) + { + this.testContext = testContext; + } + + public Task Begin() + { + testContext.BeginCalled = true; + return Task.CompletedTask; + } + + public Task End(Exception ex = null) + { + testContext.EndCalled = true; + testContext.EndException = ex; + return Task.CompletedTask; + } + + Context testContext; + } + + class MyMessageHandler : IHandleMessages + { + public Task Handle(MyMessage message, IMessageHandlerContext context) + { + throw new SimulatedException("Something went wrong"); + } + } + } + + public class MyMessage : IMessage + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_successful_message.cs b/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_successful_message.cs new file mode 100644 index 0000000000..4db02850b7 --- /dev/null +++ b/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_successful_message.cs @@ -0,0 +1,89 @@ +namespace NServiceBus.AcceptanceTests.UnitOfWork +{ + using System; + using System.Threading.Tasks; + using AcceptanceTesting; + using EndpointTemplates; + using Microsoft.Extensions.DependencyInjection; + using NServiceBus.UnitOfWork; + using NUnit.Framework; + + public class When_using_custom_unit_of_work_with_successful_message : NServiceBusAcceptanceTest + { + [Test] + public async Task Should_execute_uow() + { + var context = await Scenario.Define() + .WithEndpoint(g => g.When(b => b.SendLocal(new MyMessage()))) + .Done(c => c.BeginCalled && c.EndCalled) + .Run(); + + Assert.True(context.BeginCalled, "Unit of work should have been executed"); + Assert.True(context.EndCalled, "Unit of work should have been executed"); + Assert.IsNull(context.EndException, "Exception was provided to unit of work but should not have been"); + } + + public class Context : ScenarioContext + { + public bool Done { get; set; } + public bool BeginCalled { get; set; } + public bool EndCalled { get; set; } + public Exception EndException { get; set; } + } + + public class EndpointWithCustomUnitOfWork : EndpointConfigurationBuilder + { + public EndpointWithCustomUnitOfWork() + { + EndpointSetup((c, r) => + { + c.RegisterComponents(services => services.AddSingleton()); + }); + } + + class CustomUnitOfWork : IManageUnitsOfWork + { + public CustomUnitOfWork(Context testContext) + { + this.testContext = testContext; + } + + public Task Begin() + { + testContext.BeginCalled = true; + return Task.CompletedTask; + } + + public Task End(Exception ex = null) + { + testContext.EndCalled = true; + testContext.EndException = ex; + return Task.CompletedTask; + } + + Context testContext; + } + + class MyMessageHandler : IHandleMessages + { + public MyMessageHandler(Context testContext) + { + this.testContext = testContext; + } + + public Task Handle(MyMessage message, IMessageHandlerContext context) + { + testContext.Done = true; + + return Task.FromResult(0); + } + + Context testContext; + } + } + + public class MyMessage : IMessage + { + } + } +} \ No newline at end of file From 3ba80e27839d150be196c6d5c4afd536c3251233 Mon Sep 17 00:00:00 2001 From: danielmarbach Date: Fri, 25 Sep 2020 15:03:39 +0200 Subject: [PATCH 2/4] Add test to verify core should throw when custom uow adds a scope --- ...nit_of_work_with_wrap_handlers_in_scope.cs | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_wrap_handlers_in_scope.cs diff --git a/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_wrap_handlers_in_scope.cs b/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_wrap_handlers_in_scope.cs new file mode 100644 index 0000000000..3a9d55de5c --- /dev/null +++ b/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_wrap_handlers_in_scope.cs @@ -0,0 +1,86 @@ +namespace NServiceBus.AcceptanceTests.UnitOfWork +{ + using System; + using System.Linq; + using System.Threading.Tasks; + using System.Transactions; + using AcceptanceTesting; + using EndpointTemplates; + using Microsoft.Extensions.DependencyInjection; + using NServiceBus.UnitOfWork; + using NUnit.Framework; + + public class When_using_custom_unit_of_work_with_wrap_handlers_in_scope : NServiceBusAcceptanceTest + { + [Test] + public async Task Should_fail() + { + var context = await Scenario.Define() + .WithEndpoint(g => + { + g.DoNotFailOnErrorMessages(); + g.When(b => b.SendLocal(new MyMessage())); + }) + .Done(c => c.FailedMessages.Any()) + .Run(); + + Assert.False(context.ShouldNeverBeCalled, "Unit of work should have been executed"); + } + + public class Context : ScenarioContext + { + public bool Done { get; set; } + public bool ShouldNeverBeCalled { get; set; } + } + + public class EndpointWithCustomUnitOfWork : EndpointConfigurationBuilder + { + public EndpointWithCustomUnitOfWork() + { + EndpointSetup((c, r) => + { + c.UnitOfWork().WrapHandlersInATransactionScope(); + + c.RegisterComponents(services => services.AddSingleton()); + }); + } + + class CustomUnitOfWork : IManageUnitsOfWork + { + TransactionScope transactionScope; + public Task Begin() + { + // this only works because we are not using the async state machine + transactionScope = new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled); + return Task.CompletedTask; + } + + public Task End(Exception ex = null) + { + transactionScope.Complete(); + return Task.CompletedTask; + } + } + + class MyMessageHandler : IHandleMessages + { + Context testContext; + + public MyMessageHandler(Context testContext) + { + this.testContext = testContext; + } + + public Task Handle(MyMessage message, IMessageHandlerContext context) + { + testContext.ShouldNeverBeCalled = true; + return Task.CompletedTask; + } + } + } + + public class MyMessage : IMessage + { + } + } +} \ No newline at end of file From bfaa5bf143309cc287cc1ee9f24e0a261020c61d Mon Sep 17 00:00:00 2001 From: danielmarbach Date: Fri, 25 Sep 2020 15:04:31 +0200 Subject: [PATCH 3/4] Add explicit ordering for TransactionScopeUnitOfWorkBehavior to make it behave like 7.2 and earlier --- .../TransactionScopes/TransactionScopeUnitOfWork.cs | 2 +- .../TransactionScopeUnitOfWorkBehavior.cs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/NServiceBus.Core/UnitOfWork/TransactionScopes/TransactionScopeUnitOfWork.cs b/src/NServiceBus.Core/UnitOfWork/TransactionScopes/TransactionScopeUnitOfWork.cs index 207e083d95..bd35e48c5c 100644 --- a/src/NServiceBus.Core/UnitOfWork/TransactionScopes/TransactionScopeUnitOfWork.cs +++ b/src/NServiceBus.Core/UnitOfWork/TransactionScopes/TransactionScopeUnitOfWork.cs @@ -13,7 +13,7 @@ protected internal override void Setup(FeatureConfigurationContext context) } var transactionOptions = context.Settings.Get().TransactionOptions; - context.Pipeline.Register("HandlerTransactionScopeWrapper", new TransactionScopeUnitOfWorkBehavior(transactionOptions), "Makes sure that the handlers gets wrapped in a transaction scope"); + context.Pipeline.Register( new TransactionScopeUnitOfWorkBehavior.Registration(transactionOptions)); } public class Settings diff --git a/src/NServiceBus.Core/UnitOfWork/TransactionScopes/TransactionScopeUnitOfWorkBehavior.cs b/src/NServiceBus.Core/UnitOfWork/TransactionScopes/TransactionScopeUnitOfWorkBehavior.cs index 126b4fab1a..8281aaba07 100644 --- a/src/NServiceBus.Core/UnitOfWork/TransactionScopes/TransactionScopeUnitOfWorkBehavior.cs +++ b/src/NServiceBus.Core/UnitOfWork/TransactionScopes/TransactionScopeUnitOfWorkBehavior.cs @@ -28,5 +28,16 @@ public async Task Invoke(IIncomingPhysicalMessageContext context, Func new TransactionScopeUnitOfWorkBehavior(transactionOptions)) + { + InsertAfter("ExecuteUnitOfWork"); + } + } } } \ No newline at end of file From d578961c526f50ceb9aad0a606b8d25ab6ed566a Mon Sep 17 00:00:00 2001 From: Dennis Date: Fri, 23 Oct 2020 16:29:06 +0200 Subject: [PATCH 4/4] Fixed cherry picked commits for previous NSB version --- ...n_using_custom_unit_of_work_with_failed_message.cs | 7 +++---- ...ing_custom_unit_of_work_with_successful_message.cs | 7 +++---- ...custom_unit_of_work_with_wrap_handlers_in_scope.cs | 11 +++++------ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_failed_message.cs b/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_failed_message.cs index 2913b22156..c7404251bb 100644 --- a/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_failed_message.cs +++ b/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_failed_message.cs @@ -4,7 +4,6 @@ namespace NServiceBus.AcceptanceTests.UnitOfWork using System.Threading.Tasks; using AcceptanceTesting; using EndpointTemplates; - using Microsoft.Extensions.DependencyInjection; using NServiceBus.UnitOfWork; using NUnit.Framework; @@ -41,7 +40,7 @@ public EndpointWithCustomUnitOfWork() { EndpointSetup((c, r) => { - c.RegisterComponents(services => services.AddSingleton()); + c.RegisterComponents(container => container.ConfigureComponent(DependencyLifecycle.InstancePerCall)); }); } @@ -55,14 +54,14 @@ public CustomUnitOfWork(Context testContext) public Task Begin() { testContext.BeginCalled = true; - return Task.CompletedTask; + return Task.FromResult(0); } public Task End(Exception ex = null) { testContext.EndCalled = true; testContext.EndException = ex; - return Task.CompletedTask; + return Task.FromResult(0); } Context testContext; diff --git a/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_successful_message.cs b/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_successful_message.cs index 4db02850b7..7a74c8e2c6 100644 --- a/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_successful_message.cs +++ b/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_successful_message.cs @@ -4,7 +4,6 @@ namespace NServiceBus.AcceptanceTests.UnitOfWork using System.Threading.Tasks; using AcceptanceTesting; using EndpointTemplates; - using Microsoft.Extensions.DependencyInjection; using NServiceBus.UnitOfWork; using NUnit.Framework; @@ -37,7 +36,7 @@ public EndpointWithCustomUnitOfWork() { EndpointSetup((c, r) => { - c.RegisterComponents(services => services.AddSingleton()); + c.RegisterComponents(container => container.ConfigureComponent(DependencyLifecycle.InstancePerCall)); }); } @@ -51,14 +50,14 @@ public CustomUnitOfWork(Context testContext) public Task Begin() { testContext.BeginCalled = true; - return Task.CompletedTask; + return Task.FromResult(0); } public Task End(Exception ex = null) { testContext.EndCalled = true; testContext.EndException = ex; - return Task.CompletedTask; + return Task.FromResult(0); } Context testContext; diff --git a/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_wrap_handlers_in_scope.cs b/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_wrap_handlers_in_scope.cs index 3a9d55de5c..21f20cf7b1 100644 --- a/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_wrap_handlers_in_scope.cs +++ b/src/NServiceBus.AcceptanceTests/Core/UnitOfWork/When_using_custom_unit_of_work_with_wrap_handlers_in_scope.cs @@ -6,7 +6,6 @@ namespace NServiceBus.AcceptanceTests.UnitOfWork using System.Transactions; using AcceptanceTesting; using EndpointTemplates; - using Microsoft.Extensions.DependencyInjection; using NServiceBus.UnitOfWork; using NUnit.Framework; @@ -41,7 +40,7 @@ public EndpointWithCustomUnitOfWork() { c.UnitOfWork().WrapHandlersInATransactionScope(); - c.RegisterComponents(services => services.AddSingleton()); + c.RegisterComponents(container => container.ConfigureComponent(DependencyLifecycle.InstancePerCall)); }); } @@ -52,19 +51,19 @@ public Task Begin() { // this only works because we are not using the async state machine transactionScope = new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled); - return Task.CompletedTask; + return Task.FromResult(0); } public Task End(Exception ex = null) { transactionScope.Complete(); - return Task.CompletedTask; + return Task.FromResult(0); } } class MyMessageHandler : IHandleMessages { - Context testContext; + readonly Context testContext; public MyMessageHandler(Context testContext) { @@ -74,7 +73,7 @@ public MyMessageHandler(Context testContext) public Task Handle(MyMessage message, IMessageHandlerContext context) { testContext.ShouldNeverBeCalled = true; - return Task.CompletedTask; + return Task.FromResult(0); } } }