Skip to content

Commit

Permalink
Fix ref struct scan for sagas (#7187)
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: Mike Minutillo <mike.minutillo@particular.net>
  • Loading branch information
andreasohlund and mikeminutillo committed Oct 28, 2024
1 parent cd17f8a commit 1e982e2
Show file tree
Hide file tree
Showing 2 changed files with 92 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; }
}
}
39 changes: 39 additions & 0 deletions src/NServiceBus.Core/Sagas/Sagas.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,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 All @@ -106,4 +113,36 @@ static bool IsTypeATimeoutHandledByAnySaga(Type type, IEnumerable<Type> sagas)

Conventions conventions;
}

static bool IsSagaType(Type t)

Check failure on line 117 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Windows

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 117 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Windows

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 117 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Linux

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 117 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Linux

A namespace cannot directly contain members such as fields, methods or statements
{
return IsCompatible(t, typeof(Saga));
}

static bool IsSagaNotFoundHandler(Type t)

Check failure on line 122 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Windows

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 122 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Windows

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 122 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Linux

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 122 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Linux

A namespace cannot directly contain members such as fields, methods or statements
{
return IsCompatible(t, typeof(IHandleSagaNotFound));
}

static bool IsCompatible(Type t, Type source)

Check failure on line 127 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Windows

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 127 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Windows

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 127 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Linux

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 127 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Linux

A namespace cannot directly contain members such as fields, methods or statements
{
return source.IsAssignableFrom(t) && t != source && !t.IsAbstract && !t.IsInterface && !t.IsGenericType;
}

static bool IsTypeATimeoutHandledByAnySaga(Type type, IEnumerable<Type> sagas)

Check failure on line 132 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Windows

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 132 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Windows

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 132 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Linux

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 132 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Linux

A namespace cannot directly contain members such as fields, methods or statements
{
// 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);

return sagas.Any(t => timeoutHandler.IsAssignableFrom(t) && !messageHandler.IsAssignableFrom(t));
}

Conventions conventions;

Check failure on line 147 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Windows

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 147 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Windows

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 147 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Linux

A namespace cannot directly contain members such as fields, methods or statements

Check failure on line 147 in src/NServiceBus.Core/Sagas/Sagas.cs

View workflow job for this annotation

GitHub Actions / Linux

A namespace cannot directly contain members such as fields, methods or statements
}

0 comments on commit 1e982e2

Please sign in to comment.