Skip to content

Commit b5b5330

Browse files
andreasohlundDavidBoikedanielmarbach
authored
Explicit custom saga finder registration API (#7445)
* Add new API with naive implementation * Move towards strongly typed activation * Remove scanning of finders * Fix tests * Formatting * Cleanup * Code style * Remove unneded mapping layer * Cleanup * Remove unused class * Consolidate * Use new to create the finder adapters * Get rid of finder properties * Get rid of findertype * More cleanup * ctor cleanup * Add a mapper finalize step * Move more validation into mapper * Fix persistence tests * Cleanup * Dispose finders * Fix warnings * Comment * Fixes after rebase * Add finder type back in * Add obsoletes * More obsoletes * Formatting * Apply suggestions from code review Co-authored-by: David Boike <david.boike@gmail.com> * Approve reworded obsoletes * Fix after rebase * Simplify metadata * Cleanup mapping * Factory can be static * Simplify mapper logic * Remove TypeDescriptor.GetConverter that requires unreferenced code * Remove null handling because the code could never return null (not before and not now) * Class constraint for finder * More consistent constraint through the saga data * Same constraint * The message type is well known * The message type is well known here too * More strong typing and get rid of closure that is unnecessary * Rename to inspect since it is less misleading * Nullability on mapper and header mapping * Cleanup * Update src/NServiceBus.Core/Sagas/SagaMapper.cs Co-authored-by: David Boike <david.boike@gmail.com> * Update src/NServiceBus.Core/Sagas/SagaPropertyMapper.cs Co-authored-by: David Boike <david.boike@gmail.com> * Fix obsoletes --------- Co-authored-by: David Boike <david.boike@gmail.com> Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com>
1 parent 95c1aec commit b5b5330

File tree

47 files changed

+534
-827
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+534
-827
lines changed

src/NServiceBus.AcceptanceTesting/Customization/EndpointConfigurationExtensions.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ public static void ScanTypesForTest(this EndpointConfiguration config,
2626
var testTypes = GetNestedTypeRecursive(customizationConfiguration.BuilderType.DeclaringType, customizationConfiguration.BuilderType).ToList();
2727

2828
var typesToIncludeInScanning = testTypes
29-
.Where(t => t.IsAssignableTo(typeof(IFinder))
30-
|| t.IsAssignableTo(typeof(IHandleSagaNotFound))
29+
.Where(t => t.IsAssignableTo(typeof(IHandleSagaNotFound))
3130
|| t.IsAssignableTo(typeof(Saga)))
3231
.Union(customizationConfiguration.TypesToInclude);
3332

src/NServiceBus.AcceptanceTests/Core/Sagas/When_adding_state_to_context.cs

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,51 +31,40 @@ public async Task Should_make_state_available_to_finder_context()
3131
}
3232
}
3333

34-
public class Context : ScenarioContext
34+
class Context : ScenarioContext
3535
{
3636
public bool FinderUsed { get; set; }
3737
public IReadOnlyContextBag ContextBag { get; set; }
3838
}
3939

4040
public class SagaEndpoint : EndpointConfigurationBuilder
4141
{
42-
public SagaEndpoint()
43-
{
42+
public SagaEndpoint() =>
4443
EndpointSetup<DefaultServer>(c =>
4544
{
4645
//use InMemoryPersistence as custom finder support is required
4746
c.UsePersistence<AcceptanceTestingPersistence>();
4847
c.Pipeline.Register(new BehaviorWhichAddsThingsToTheContext(), "adds some data to the context");
4948
});
50-
}
5149

52-
class CustomFinder : ISagaFinder<TestSaga07.SagaData07, StartSagaMessage>
50+
class CustomFinder(Context testContext) : ISagaFinder<TestSaga07.SagaData07, StartSagaMessage>
5351
{
54-
public CustomFinder(Context testContext)
55-
{
56-
this.testContext = testContext;
57-
}
58-
5952
public Task<TestSaga07.SagaData07> FindBy(StartSagaMessage message, ISynchronizedStorageSession storageSession, IReadOnlyContextBag context, CancellationToken cancellationToken = default)
6053
{
6154
testContext.ContextBag = context;
6255
testContext.FinderUsed = true;
6356
return Task.FromResult(default(TestSaga07.SagaData07));
6457
}
65-
66-
Context testContext;
6758
}
6859

6960
public class TestSaga07 : Saga<TestSaga07.SagaData07>, IAmStartedByMessages<StartSagaMessage>
7061
{
71-
public Task Handle(StartSagaMessage message, IMessageHandlerContext context)
72-
{
73-
return Task.CompletedTask;
74-
}
62+
public Task Handle(StartSagaMessage message, IMessageHandlerContext context) => Task.CompletedTask;
7563

7664
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<SagaData07> mapper)
7765
{
7866
// custom finder used
67+
mapper.ConfigureFinderMapping<StartSagaMessage, CustomFinder>();
7968
}
8069

8170
public class SagaData07 : ContainSagaData

src/NServiceBus.AcceptanceTests/Core/Sagas/When_finder_cant_find_saga_instance.cs

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -36,40 +36,22 @@ public class Context : ScenarioContext
3636

3737
public class SagaEndpoint : EndpointConfigurationBuilder
3838
{
39-
public SagaEndpoint()
40-
{
41-
EndpointSetup<DefaultServer>(c =>
42-
{
43-
//use InMemoryPersistence as custom finder support is required
44-
c.UsePersistence<AcceptanceTestingPersistence>();
45-
});
46-
}
39+
public SagaEndpoint() =>
40+
EndpointSetup<DefaultServer>(c => c.UsePersistence<AcceptanceTestingPersistence>());
4741

48-
class CustomFinder : ISagaFinder<TestSaga06.SagaData06, StartSagaMessage>
42+
class CustomFinder(Context testContext) : ISagaFinder<TestSaga06.SagaData06, StartSagaMessage>
4943
{
50-
public CustomFinder(Context testContext)
51-
{
52-
this.testContext = testContext;
53-
}
54-
5544
public Task<TestSaga06.SagaData06> FindBy(StartSagaMessage message, ISynchronizedStorageSession storageSession, IReadOnlyContextBag context, CancellationToken cancellationToken = default)
5645
{
5746
testContext.FinderUsed = true;
5847
return Task.FromResult(default(TestSaga06.SagaData06));
5948
}
60-
61-
Context testContext;
6249
}
6350

64-
public class TestSaga06 : Saga<TestSaga06.SagaData06>,
51+
public class TestSaga06(Context testContext) : Saga<TestSaga06.SagaData06>,
6552
IAmStartedByMessages<StartSagaMessage>,
6653
IHandleMessages<SomeOtherMessage>
6754
{
68-
public TestSaga06(Context testContext)
69-
{
70-
this.testContext = testContext;
71-
}
72-
7355
public Task Handle(StartSagaMessage message, IMessageHandlerContext context)
7456
{
7557
// need to set the correlation property manually because the finder doesn't return an existing instance
@@ -81,22 +63,17 @@ public Task Handle(StartSagaMessage message, IMessageHandlerContext context)
8163

8264
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<SagaData06> mapper)
8365
{
84-
// no mapping for StartSagaMessage required because of CustomFinder
66+
mapper.ConfigureFinderMapping<StartSagaMessage, CustomFinder>();
8567
mapper.ConfigureMapping<SomeOtherMessage>(m => m.CorrelationProperty).ToSaga(s => s.CorrelationProperty);
8668
}
8769

88-
// This additional, unused, message is required to reprododuce https://github.com/Particular/NServiceBus/issues/4888
89-
public Task Handle(SomeOtherMessage message, IMessageHandlerContext context)
90-
{
91-
return Task.CompletedTask;
92-
}
70+
// This additional, unused, message is required to reproduce https://github.com/Particular/NServiceBus/issues/4888
71+
public Task Handle(SomeOtherMessage message, IMessageHandlerContext context) => Task.CompletedTask;
9372

9473
public class SagaData06 : ContainSagaData
9574
{
9675
public virtual string CorrelationProperty { get; set; }
9776
}
98-
99-
Context testContext;
10077
}
10178
}
10279

src/NServiceBus.AcceptanceTests/Core/Sagas/When_finder_returns_existing_saga.cs

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,46 +37,27 @@ public class Context : ScenarioContext
3737

3838
public class SagaEndpoint : EndpointConfigurationBuilder
3939
{
40-
public SagaEndpoint()
41-
{
42-
EndpointSetup<DefaultServer>();
43-
}
40+
public SagaEndpoint() => EndpointSetup<DefaultServer>();
4441

45-
public class CustomFinder : ISagaFinder<TestSaga08.SagaData08, SomeOtherMessage>
42+
public class CustomFinder(Context testContext, ISagaPersister sagaPersister) : ISagaFinder<TestSaga08.SagaData08, SomeOtherMessage>
4643
{
47-
public CustomFinder(Context testContext, ISagaPersister sagaPersister)
48-
{
49-
this.testContext = testContext;
50-
this.sagaPersister = sagaPersister;
51-
}
52-
5344
public async Task<TestSaga08.SagaData08> FindBy(SomeOtherMessage message, ISynchronizedStorageSession storageSession, IReadOnlyContextBag context, CancellationToken cancellationToken = default)
5445
{
5546
testContext.FinderUsed = true;
5647
var sagaData = await sagaPersister.Get<TestSaga08.SagaData08>(message.SagaId, storageSession, (ContextBag)context, cancellationToken).ConfigureAwait(false);
5748
return sagaData;
5849
}
59-
60-
Context testContext;
61-
ISagaPersister sagaPersister;
6250
}
6351

64-
public class TestSaga08 : Saga<TestSaga08.SagaData08>,
52+
public class TestSaga08(Context testContext) : Saga<TestSaga08.SagaData08>,
6553
IAmStartedByMessages<StartSagaMessage>,
6654
IHandleMessages<SomeOtherMessage>
6755
{
68-
public TestSaga08(Context testContext)
69-
{
70-
this.testContext = testContext;
71-
}
72-
73-
public Task Handle(StartSagaMessage message, IMessageHandlerContext context)
74-
{
75-
return context.SendLocal(new SomeOtherMessage
56+
public Task Handle(StartSagaMessage message, IMessageHandlerContext context) =>
57+
context.SendLocal(new SomeOtherMessage
7658
{
7759
SagaId = Data.Id
7860
});
79-
}
8061

8162
public Task Handle(SomeOtherMessage message, IMessageHandlerContext context)
8263
{
@@ -87,15 +68,13 @@ public Task Handle(SomeOtherMessage message, IMessageHandlerContext context)
8768
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<SagaData08> mapper)
8869
{
8970
mapper.ConfigureMapping<StartSagaMessage>(saga => saga.Property).ToSaga(saga => saga.Property);
90-
// Mapping not required for SomeOtherMessage because CustomFinder used
71+
mapper.ConfigureFinderMapping<SomeOtherMessage, CustomFinder>();
9172
}
9273

9374
public class SagaData08 : ContainSagaData
9475
{
9576
public virtual string Property { get; set; }
9677
}
97-
98-
Context testContext;
9978
}
10079
}
10180

src/NServiceBus.AcceptanceTests/Core/SelfVerification/When_running_saga_tests.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,7 @@ public void All_saga_entities_in_acceptance_tests_should_have_virtual_properties
2525
{
2626
foreach (var property in entity.GetProperties())
2727
{
28-
if (property.GetGetMethod().IsVirtual)
29-
{
30-
Console.WriteLine("OK: {0}.{1}", entity.FullName, property.Name);
31-
}
32-
else
28+
if (!property.GetGetMethod().IsVirtual)
3329
{
3430
offenders++;
3531
Console.WriteLine("ERROR: {0}.{1} must be marked as virtual for NHibernate tests to succeed.", entity.FullName, property.Name);

src/NServiceBus.Core.Tests/ApprovalFiles/APIApprovals.ApproveNServiceBus.approved.txt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -391,15 +391,21 @@ namespace NServiceBus
391391
{
392392
System.Threading.CancellationToken CancellationToken { get; }
393393
}
394+
public interface IConfigureHowToFindSagaWithFinder
395+
{
396+
void ConfigureMapping<TSagaEntity, TMessage, TFinder>()
397+
where TSagaEntity : class, NServiceBus.IContainSagaData
398+
where TFinder : class, NServiceBus.Sagas.ISagaFinder<TSagaEntity, TMessage>;
399+
}
394400
public interface IConfigureHowToFindSagaWithMessage
395401
{
396402
void ConfigureMapping<TSagaEntity, TMessage>(System.Linq.Expressions.Expression<System.Func<TSagaEntity, object?>> sagaEntityProperty, System.Linq.Expressions.Expression<System.Func<TMessage, object?>> messageProperty)
397-
where TSagaEntity : NServiceBus.IContainSagaData;
403+
where TSagaEntity : class, NServiceBus.IContainSagaData;
398404
}
399405
public interface IConfigureHowToFindSagaWithMessageHeaders
400406
{
401-
void ConfigureMapping<TSagaEntity, TMessage>(System.Linq.Expressions.Expression<System.Func<TSagaEntity, object>> sagaEntityProperty, string headerName)
402-
where TSagaEntity : NServiceBus.IContainSagaData;
407+
void ConfigureMapping<TSagaEntity, TMessage>(System.Linq.Expressions.Expression<System.Func<TSagaEntity, object?>> sagaEntityProperty, string headerName)
408+
where TSagaEntity : class, NServiceBus.IContainSagaData;
403409
}
404410
public interface IContainSagaData
405411
{
@@ -869,6 +875,8 @@ namespace NServiceBus
869875
public class SagaPropertyMapper<TSagaData>
870876
where TSagaData : class, NServiceBus.IContainSagaData
871877
{
878+
public void ConfigureFinderMapping<TMessage, TFinder>()
879+
where TFinder : class, NServiceBus.Sagas.ISagaFinder<TSagaData, TMessage> { }
872880
public NServiceBus.IToSagaExpression<TSagaData> ConfigureHeaderMapping<TMessage>(string headerName) { }
873881
public NServiceBus.ToSagaExpression<TSagaData, TMessage> ConfigureMapping<TMessage>(System.Linq.Expressions.Expression<System.Func<TMessage, object?>> messageProperty) { }
874882
public NServiceBus.CorrelatedSagaPropertyMapper<TSagaData> MapSaga(System.Linq.Expressions.Expression<System.Func<TSagaData, object?>> sagaProperty) { }
@@ -2046,7 +2054,9 @@ namespace NServiceBus.Sagas
20462054
public class SagaFinderDefinition
20472055
{
20482056
public System.Type MessageType { get; }
2057+
[System.Obsolete("Use MessageType.FullName instead. Will be removed in version 11.0.0.", true)]
20492058
public string MessageTypeName { get; }
2059+
[System.Obsolete("Finder properties are no longer used. Will be removed in version 11.0.0.", true)]
20502060
public System.Collections.Generic.Dictionary<string, object> Properties { get; }
20512061
public System.Type Type { get; }
20522062
}
@@ -2065,6 +2075,8 @@ namespace NServiceBus.Sagas
20652075
}
20662076
public class SagaMetadata
20672077
{
2078+
[System.Obsolete("Use SagaMetadata.Create to create metadata objects. Will be removed in version 11" +
2079+
".0.0.", true)]
20682080
public SagaMetadata(string name, System.Type sagaType, string entityName, System.Type sagaEntityType, NServiceBus.Sagas.SagaMetadata.CorrelationPropertyMetadata correlationProperty, System.Collections.Generic.IReadOnlyCollection<NServiceBus.Sagas.SagaMessage> messages, System.Collections.Generic.IReadOnlyCollection<NServiceBus.Sagas.SagaFinderDefinition> finders) { }
20692081
public System.Collections.Generic.IReadOnlyCollection<NServiceBus.Sagas.SagaMessage> AssociatedMessages { get; }
20702082
public string EntityName { get; }
@@ -2076,6 +2088,8 @@ namespace NServiceBus.Sagas
20762088
public bool TryGetCorrelationProperty(out NServiceBus.Sagas.SagaMetadata.CorrelationPropertyMetadata property) { }
20772089
public bool TryGetFinder(string messageType, out NServiceBus.Sagas.SagaFinderDefinition finderDefinition) { }
20782090
public static NServiceBus.Sagas.SagaMetadata Create(System.Type sagaType) { }
2091+
[System.Obsolete("Use the overload without available types and conventions. Will be removed in vers" +
2092+
"ion 11.0.0.", true)]
20792093
public static NServiceBus.Sagas.SagaMetadata Create(System.Type sagaType, System.Collections.Generic.IEnumerable<System.Type> availableTypes, NServiceBus.Conventions conventions) { }
20802094
public class CorrelationPropertyMetadata
20812095
{
@@ -2091,6 +2105,8 @@ namespace NServiceBus.Sagas
20912105
public NServiceBus.Sagas.SagaMetadata FindByEntity(System.Type entityType) { }
20922106
public System.Collections.Generic.IEnumerator<NServiceBus.Sagas.SagaMetadata> GetEnumerator() { }
20932107
public void Initialize(System.Collections.Generic.IEnumerable<System.Type> availableTypes) { }
2108+
[System.Obsolete("Use the overload without available types and conventions. Will be removed in vers" +
2109+
"ion 11.0.0.", true)]
20942110
public void Initialize(System.Collections.Generic.IEnumerable<System.Type> availableTypes, NServiceBus.Conventions conventions) { }
20952111
}
20962112
public class SagaSettings : NServiceBus.Configuration.AdvancedExtensibility.ExposeSettings

src/NServiceBus.Core.Tests/ApprovalFiles/When_saga_has_multiple_correlated_properties.Should_throw.approved.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/NServiceBus.Core.Tests/Pipeline/Incoming/InvokeHandlerTerminatorTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public void Should_throw_friendly_exception_if_handler_returns_null()
114114

115115
static ActiveSagaInstance AssociateSagaWithMessage(FakeSaga saga, TestableInvokeHandlerContext behaviorContext)
116116
{
117-
var sagaInstance = new ActiveSagaInstance(saga, SagaMetadata.Create(typeof(FakeSaga), [], new Conventions()), () => DateTime.UtcNow);
117+
var sagaInstance = new ActiveSagaInstance(saga, SagaMetadata.Create(typeof(FakeSaga)), () => DateTime.UtcNow);
118118
behaviorContext.Extensions.Set(sagaInstance);
119119
return sagaInstance;
120120
}

0 commit comments

Comments
 (0)