Skip to content

Commit

Permalink
Fix ref struct scan for sagas (#7187) (#7192)
Browse files Browse the repository at this point in the history
* Prevent saga conventions from throwing on ref struct

* Better test

* Tweaks

---------

Co-authored-by: Andreas Öhlund <[email protected]>
Co-authored-by: Mike Minutillo <[email protected]>
  • Loading branch information
3 people authored Oct 25, 2024
1 parent a5b0048 commit e5a7a8d
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace NServiceBus.AcceptanceTests.Core.Conventions;

using System;
using System.Threading.Tasks;
using AcceptanceTesting;
using EndpointTemplates;
using NServiceBus.AcceptanceTesting.Customization;
using NUnit.Framework;

public class When_scanning_an_assembly_containing_a_ref_struct_and_sagas_enabled : NServiceBusAcceptanceTest
{
[Test]
public void It_should_not_throw_an_exception()
=> Assert.DoesNotThrowAsync(
() => Scenario.Define<ScenarioContext>()
.WithEndpoint<EndpointWithASaga>()
.Run()
);

// HINT: This will get picked up by the AssemblyRouteSource created by the routing call below
// Even though it is not a message type, it is still checked by passing it to conventions.
// The conventions added by Sagas were throwing an exception when passed a ref struct.
// See https://github.com/Particular/NServiceBus/issues/7179 for details.
ref struct RefStruct { }

class EndpointWithASaga : EndpointConfigurationBuilder
{
public EndpointWithASaga() => EndpointSetup<DefaultServer>(cfg => cfg
.ConfigureRouting()
.RouteToEndpoint(
typeof(RefStruct).Assembly,
Conventions.EndpointNamingConvention(typeof(EndpointWithASaga))
)
);

class RealSagaToSetUpConventions : Saga<RealSagaToSetUpConventions.RealSagaToSetUpConventionsSagaData>, IAmStartedByMessages<SomeMessage>
{
public Task Handle(SomeMessage message, IMessageHandlerContext context) => Task.CompletedTask;
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<RealSagaToSetUpConventionsSagaData> mapper)
=> mapper.MapSaga(saga => saga.BusinessId).ToMessage<SomeMessage>(msg => msg.BusinessId);

public class RealSagaToSetUpConventionsSagaData : ContainSagaData
{
public virtual Guid BusinessId { get; set; }
}
}
}

public class SomeMessage : IMessage
{
public Guid BusinessId { get; set; }
}
}
7 changes: 7 additions & 0 deletions src/NServiceBus.Core/Sagas/Sagas.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ static bool IsCompatible(Type t, Type source)

static bool IsTypeATimeoutHandledByAnySaga(Type type, IEnumerable<Type> sagas)
{
// MakeGenericType() throws an exception if passed a ref struct type
// Messages cannot be ref struct types
if (type.IsByRefLike)
{
return false;
}

var timeoutHandler = typeof(IHandleTimeouts<>).MakeGenericType(type);
var messageHandler = typeof(IHandleMessages<>).MakeGenericType(type);

Expand Down

0 comments on commit e5a7a8d

Please sign in to comment.