Skip to content

Commit 91c73f5

Browse files
Ref struct in scanned assembly results in app crash at startup, if sagas are present (#7195)
* Fix ref struct scan for sagas (#7187) * Prevent saga conventions from throwing on ref struct * Better test * Tweaks --------- Co-authored-by: Mike Minutillo <[email protected]> * Handle netframework * Use fx agnostic way --------- Co-authored-by: Mike Minutillo <[email protected]>
1 parent 8305ddd commit 91c73f5

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
namespace NServiceBus.AcceptanceTests.Core.Conventions;
2+
3+
using System;
4+
using System.Threading.Tasks;
5+
using AcceptanceTesting;
6+
using EndpointTemplates;
7+
using NServiceBus.AcceptanceTesting.Customization;
8+
using NUnit.Framework;
9+
10+
public class When_scanning_an_assembly_containing_a_ref_struct_and_sagas_enabled : NServiceBusAcceptanceTest
11+
{
12+
[Test]
13+
public void It_should_not_throw_an_exception()
14+
=> Assert.DoesNotThrowAsync(
15+
() => Scenario.Define<ScenarioContext>()
16+
.WithEndpoint<EndpointWithASaga>()
17+
.Run()
18+
);
19+
20+
// HINT: This will get picked up by the AssemblyRouteSource created by the routing call below
21+
// Even though it is not a message type, it is still checked by passing it to conventions.
22+
// The conventions added by Sagas were throwing an exception when passed a ref struct.
23+
// See https://github.com/Particular/NServiceBus/issues/7179 for details.
24+
ref struct RefStruct { }
25+
26+
class EndpointWithASaga : EndpointConfigurationBuilder
27+
{
28+
public EndpointWithASaga() => EndpointSetup<DefaultServer>(cfg => cfg
29+
.ConfigureRouting()
30+
.RouteToEndpoint(
31+
typeof(RefStruct).Assembly,
32+
Conventions.EndpointNamingConvention(typeof(EndpointWithASaga))
33+
)
34+
);
35+
36+
class RealSagaToSetUpConventions : Saga<RealSagaToSetUpConventions.RealSagaToSetUpConventionsSagaData>, IAmStartedByMessages<SomeMessage>
37+
{
38+
public Task Handle(SomeMessage message, IMessageHandlerContext context) => Task.CompletedTask;
39+
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<RealSagaToSetUpConventionsSagaData> mapper)
40+
=> mapper.MapSaga(saga => saga.BusinessId).ToMessage<SomeMessage>(msg => msg.BusinessId);
41+
42+
public class RealSagaToSetUpConventionsSagaData : ContainSagaData
43+
{
44+
public virtual Guid BusinessId { get; set; }
45+
}
46+
}
47+
}
48+
49+
public class SomeMessage : IMessage
50+
{
51+
public Guid BusinessId { get; set; }
52+
}
53+
}

src/NServiceBus.Core/Sagas/Sagas.cs

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Linq;
6+
using System.Reflection;
7+
using System.Runtime.CompilerServices;
68
using Microsoft.Extensions.DependencyInjection;
79
using NServiceBus.Sagas;
810

@@ -104,6 +106,13 @@ static bool IsCompatible(Type t, Type source)
104106

105107
static bool IsTypeATimeoutHandledByAnySaga(Type type, IEnumerable<Type> sagas)
106108
{
109+
// MakeGenericType() throws an exception if passed a ref struct type
110+
// Messages cannot be ref struct types
111+
if (type.GetCustomAttribute<IsByRefLikeAttribute>() != null)
112+
{
113+
return false;
114+
}
115+
107116
var timeoutHandler = typeof(IHandleTimeouts<>).MakeGenericType(type);
108117
var messageHandler = typeof(IHandleMessages<>).MakeGenericType(type);
109118

0 commit comments

Comments
 (0)