Skip to content

Commit

Permalink
Merge pull request #5810 from Particular/7.3-pipeline-uow-ordering
Browse files Browse the repository at this point in the history
NServiceBus 7.3 Pipeline UOW ordering fix
  • Loading branch information
yvesgoeleven committed Oct 26, 2020
2 parents b6d2c8b + d578961 commit dfee0fe
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 1 deletion.
@@ -0,0 +1,83 @@
namespace NServiceBus.AcceptanceTests.UnitOfWork
{
using System;
using System.Threading.Tasks;
using AcceptanceTesting;
using EndpointTemplates;
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<Context>()
.WithEndpoint<EndpointWithCustomUnitOfWork>(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<SimulatedException>().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<DefaultServer>((c, r) =>
{
c.RegisterComponents(container => container.ConfigureComponent<CustomUnitOfWork>(DependencyLifecycle.InstancePerCall));
});
}

class CustomUnitOfWork : IManageUnitsOfWork
{
public CustomUnitOfWork(Context testContext)
{
this.testContext = testContext;
}

public Task Begin()
{
testContext.BeginCalled = true;
return Task.FromResult(0);
}

public Task End(Exception ex = null)
{
testContext.EndCalled = true;
testContext.EndException = ex;
return Task.FromResult(0);
}

Context testContext;
}

class MyMessageHandler : IHandleMessages<MyMessage>
{
public Task Handle(MyMessage message, IMessageHandlerContext context)
{
throw new SimulatedException("Something went wrong");
}
}
}

public class MyMessage : IMessage
{
}
}
}
@@ -0,0 +1,88 @@
namespace NServiceBus.AcceptanceTests.UnitOfWork
{
using System;
using System.Threading.Tasks;
using AcceptanceTesting;
using EndpointTemplates;
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<Context>()
.WithEndpoint<EndpointWithCustomUnitOfWork>(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<DefaultServer>((c, r) =>
{
c.RegisterComponents(container => container.ConfigureComponent<CustomUnitOfWork>(DependencyLifecycle.InstancePerCall));
});
}

class CustomUnitOfWork : IManageUnitsOfWork
{
public CustomUnitOfWork(Context testContext)
{
this.testContext = testContext;
}

public Task Begin()
{
testContext.BeginCalled = true;
return Task.FromResult(0);
}

public Task End(Exception ex = null)
{
testContext.EndCalled = true;
testContext.EndException = ex;
return Task.FromResult(0);
}

Context testContext;
}

class MyMessageHandler : IHandleMessages<MyMessage>
{
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
{
}
}
}
@@ -0,0 +1,85 @@
namespace NServiceBus.AcceptanceTests.UnitOfWork
{
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Transactions;
using AcceptanceTesting;
using EndpointTemplates;
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<Context>()
.WithEndpoint<EndpointWithCustomUnitOfWork>(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<DefaultServer>((c, r) =>
{
c.UnitOfWork().WrapHandlersInATransactionScope();
c.RegisterComponents(container => container.ConfigureComponent<CustomUnitOfWork>(DependencyLifecycle.InstancePerCall));
});
}

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.FromResult(0);
}

public Task End(Exception ex = null)
{
transactionScope.Complete();
return Task.FromResult(0);
}
}

class MyMessageHandler : IHandleMessages<MyMessage>
{
readonly Context testContext;

public MyMessageHandler(Context testContext)
{
this.testContext = testContext;
}

public Task Handle(MyMessage message, IMessageHandlerContext context)
{
testContext.ShouldNeverBeCalled = true;
return Task.FromResult(0);
}
}
}

public class MyMessage : IMessage
{
}
}
}
Expand Up @@ -13,7 +13,7 @@ protected internal override void Setup(FeatureConfigurationContext context)
}

var transactionOptions = context.Settings.Get<Settings>().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
Expand Down
Expand Up @@ -28,5 +28,16 @@ public async Task Invoke(IIncomingPhysicalMessageContext context, Func<IIncoming
}

readonly TransactionOptions transactionOptions;

public class Registration : RegisterStep
{
public Registration(TransactionOptions transactionOptions) : base("HandlerTransactionScopeWrapper",
typeof(TransactionScopeUnitOfWorkBehavior),
"Makes sure that the handlers gets wrapped in a transaction scope",
b => new TransactionScopeUnitOfWorkBehavior(transactionOptions))
{
InsertAfter("ExecuteUnitOfWork");
}
}
}
}

0 comments on commit dfee0fe

Please sign in to comment.