public WhenGettingEventCorrelationId() { var configuration = new SagaConfiguration(typeof(FakeSaga)); configuration.CanStartWith((FakeInitiatingEvent e) => e.Id); configuration.CanHandle((FakeHandledEvent e) => e.Id); sagaMetadata = configuration.GetMetadata(); }
public void Should_throw() { if (Environment.OSVersion.Platform != PlatformID.Win32NT) { Assert.Ignore("ApprovalTests only works on Windows"); } var exception = Assert.Throws <Exception>(() => SagaMetadata.Create(typeof(SagaWithMultipleCorrelatedProperties), new List <Type>(), new Conventions())); TestApprover.Verify(exception.Message); }
public void When_message_only_has_custom_finder() { var availableTypes = new List <Type> { typeof(SagaWithFinderOnly.Finder) }; var metadata = SagaMetadata.Create(typeof(SagaWithFinderOnly), availableTypes, new Conventions()); Assert.AreEqual(1, metadata.Finders.Count); Assert.AreEqual(typeof(CustomFinderAdapter <SagaWithFinderOnly.SagaData, SagaWithFinderOnly.StartSagaMessage>), metadata.Finders.First().Type); }
SagaFinderDefinition GetFinder(SagaMetadata metadata, string messageType) { SagaFinderDefinition finder; if (!metadata.TryGetFinder(messageType, out finder)) { throw new Exception("Finder not found"); } return(finder); }
public void DetectAndRegisterHeaderFinders() { var metadata = SagaMetadata.Create(typeof(MySagaWithMappedHeader)); var finder = GetFinder(metadata, typeof(SomeMessage).FullName); Assert.AreEqual(typeof(HeaderPropertySagaFinder <MySagaWithMappedHeader.SagaData>), finder.Type); Assert.AreEqual("CorrelationHeader", finder.Properties["message-header-name"]); Assert.AreEqual("UniqueProperty", finder.Properties["saga-property-name"]); Assert.AreEqual(typeof(int), finder.Properties["saga-property-type"]); }
SagaFinderDefinition GetSagaFinder(SagaMetadata metadata, IInvokeHandlerContext context) { foreach (var messageType in context.MessageMetadata.MessageHierarchy) { if (metadata.TryGetFinder(messageType.FullName, out var finderDefinition)) { return(finderDefinition); } } return(null); }
/// <summary> /// Initializes a new instance of <see cref="SagaEventHandler"/>. /// </summary> /// <param name="eventHandler">The base event handler to decorate.</param> /// <param name="sagaMetadata">The saga metadata associated with this saga event handler.</param> /// <param name="sagaStore">The saga store used to load/save saga state.</param> /// <param name="commandPublisher">The command publisher used to publish saga commands.</param> /// <param name="settings">The event processor settings.</param> internal SagaEventHandler(EventHandler eventHandler, SagaMetadata sagaMetadata, IStoreSagas sagaStore, Lazy<IPublishCommands> commandPublisher, IStoreSagaSettings settings) : base(eventHandler) { Verify.NotNull(sagaStore, nameof(sagaStore)); Verify.NotNull(sagaMetadata, nameof(sagaMetadata)); Verify.NotNull(commandPublisher, nameof(commandPublisher)); Verify.NotNull(settings, nameof(settings)); this.sagaStore = sagaStore; this.sagaMetadata = sagaMetadata; this.lazyCommandPublisher = commandPublisher; }
protected UsingSagaEventHandlerBase() { var executor = new Action<Object, Event>((handler, e) => { ((FakeSaga)handler).Handle((FakeEvent)e); Handled = true; }); SagaId = GuidStrategy.NewGuid(); AggregateId = GuidStrategy.NewGuid(); Event = new FakeEvent { Id = SagaId }; SagaMetadata = new FakeSaga().GetMetadata(); EventContext = new EventContext(AggregateId, HeaderCollection.Empty, Event); EventHandler = new EventHandler(typeof(FakeSaga), typeof(FakeEvent), executor, () => { throw new NotSupportedException(); }); SagaEventHandler = new SagaEventHandler(EventHandler, SagaMetadata, SagaStore.Object, new Lazy<IPublishCommands>(() => CommandPublisher.Object)); }
void AddEntitiesToBeMapped(SagaMetadata sagaMetadata, Type rootEntity) { if (entityTypes.ContainsKey(rootEntity)) { return; } if (rootEntity.IsAbstract && rootEntity != typeof(ContainSagaData)) { return; //We skip user abstract classes } entityTypes.Add(rootEntity, sagaMetadata); var propertyInfos = rootEntity.GetProperties(); foreach (var propertyInfo in propertyInfos) { if (propertyInfo.PropertyType.GetProperty("Id") != null) { AddEntitiesToBeMapped(sagaMetadata, propertyInfo.PropertyType); } if (propertyInfo.PropertyType.IsGenericType) { var args = propertyInfo.PropertyType.GetGenericArguments(); if (args[0].GetProperty("Id") != null) { AddEntitiesToBeMapped(sagaMetadata, args[0]); } } if (rootEntity.BaseType != typeof(object) && HasAttribute <RowVersionAttribute>(propertyInfo)) { throw new MappingException(string.Format("RowVersionAttribute is not supported on derived classes, please remove RowVersionAttribute from '{0}' or derive directly from IContainSagaData", rootEntity)); } } var derivedTypes = typesToScan.Where(t => t.IsSubclassOf(rootEntity)); foreach (var derivedType in derivedTypes) { AddEntitiesToBeMapped(sagaMetadata, derivedType); } var superClasses = typesToScan.Where(t => t.IsAssignableFrom(rootEntity)); foreach (var superClass in superClasses) { AddEntitiesToBeMapped(sagaMetadata, superClass); } }
public void DetectAndRegisterCustomFindersUsingScanning() { var availableTypes = new List <Type> { typeof(MySagaWithScannedFinder.CustomFinder) }; var metadata = SagaMetadata.Create(typeof(MySagaWithScannedFinder), availableTypes, new Conventions()); var finder = GetFinder(metadata, typeof(SomeMessage).FullName); Assert.AreEqual(typeof(CustomFinderAdapter <MySagaWithScannedFinder.SagaData, SomeMessage>), finder.Type); Assert.AreEqual(typeof(MySagaWithScannedFinder.CustomFinder), finder.Properties["custom-finder-clr-type"]); }
RuntimeSagaInfo BuildSagaInfo(Type sagaDataType, SagaMetadata metadata) { return(new RuntimeSagaInfo( sagaDataType: sagaDataType, versionSpecificSettings: versionSpecificSettings, metadata: metadata, jsonSerializer: jsonSerializer, readerCreator: readerCreator, writerCreator: writerCreator, tablePrefix: tablePrefix, sqlDialect: sqlDialect, nameFilter: nameFilter)); }
public static SagaCorrelationProperty GetMetadata <T>(IContainSagaData entity) { var metadata = SagaMetadata.Create(typeof(T)); if (!metadata.TryGetCorrelationProperty(out var correlatedProp)) { return(SagaCorrelationProperty.None); } var prop = entity.GetType().GetProperty(correlatedProp.Name); var value = prop.GetValue(entity); return(new SagaCorrelationProperty(correlatedProp.Name, value)); }
protected UsingSagaEventHandlerBase() { var executor = new Action <Object, Event>((handler, e) => { ((FakeSaga)handler).Handle((FakeEvent)e); Handled = true; }); SagaId = GuidStrategy.NewGuid(); AggregateId = GuidStrategy.NewGuid(); Event = new FakeEvent { Id = SagaId }; SagaMetadata = new FakeSaga().GetMetadata(); EventContext = new EventContext(AggregateId, HeaderCollection.Empty, Event); EventHandler = new EventHandler(typeof(FakeSaga), typeof(FakeEvent), executor, () => { throw new NotSupportedException(); }); SagaEventHandler = new SagaEventHandler(EventHandler, SagaMetadata, SagaStore.Object, new Lazy <IPublishCommands>(() => CommandPublisher.Object)); }
public static SagaCorrelationProperty CreateMetadata <T>(this RavenDBPersistenceTestBase test, IContainSagaData sagaEntity) { var metadata = SagaMetadata.Create(typeof(T)); SagaMetadata.CorrelationPropertyMetadata correlationPropertyMetadata; metadata.TryGetCorrelationProperty(out correlationPropertyMetadata); var propertyInfo = metadata.SagaEntityType.GetProperty(correlationPropertyMetadata.Name); var value = propertyInfo.GetValue(sagaEntity); var correlationProperty = new SagaCorrelationProperty(correlationPropertyMetadata.Name, value); return(correlationProperty); }
public RuntimeSagaInfo( Type sagaDataType, RetrieveVersionSpecificJsonSettings versionSpecificSettings, SagaMetadata metadata, JsonSerializer jsonSerializer, Func <TextReader, JsonReader> readerCreator, Func <TextWriter, JsonWriter> writerCreator, string tablePrefix, SqlDialect sqlDialect, Func <string, string> nameFilter) { this.sagaDataType = sagaDataType; if (versionSpecificSettings != null) { deserializers = new ConcurrentDictionary <Version, JsonSerializer>(); } this.versionSpecificSettings = versionSpecificSettings; SagaType = metadata.SagaType; this.jsonSerializer = jsonSerializer; this.readerCreator = readerCreator; this.writerCreator = writerCreator; this.sqlDialect = sqlDialect; CurrentVersion = sagaDataType.Assembly.GetFileVersion(); var sqlSagaAttributeData = SqlSagaTypeDataReader.GetTypeData(metadata); var tableSuffix = nameFilter(sqlSagaAttributeData.TableSuffix); TableName = sqlDialect.GetSagaTableName(tablePrefix, tableSuffix); CompleteCommand = sqlDialect.BuildCompleteCommand(TableName); SelectFromCommandBuilder = sqlDialect.BuildSelectFromCommand(TableName); GetBySagaIdCommand = sqlDialect.BuildGetBySagaIdCommand(TableName); SaveCommand = sqlDialect.BuildSaveCommand(sqlSagaAttributeData.CorrelationProperty, sqlSagaAttributeData.TransitionalCorrelationProperty, TableName); UpdateCommand = sqlDialect.BuildUpdateCommand(sqlSagaAttributeData.TransitionalCorrelationProperty, TableName); CorrelationProperty = sqlSagaAttributeData.CorrelationProperty; HasCorrelationProperty = CorrelationProperty != null; if (HasCorrelationProperty) { GetByCorrelationPropertyCommand = sqlDialect.BuildGetByPropertyCommand(sqlSagaAttributeData.CorrelationProperty, TableName); } TransitionalCorrelationProperty = sqlSagaAttributeData.TransitionalCorrelationProperty; HasTransitionalCorrelationProperty = TransitionalCorrelationProperty != null; if (HasTransitionalCorrelationProperty) { TransitionalAccessor = sagaDataType.GetPropertyAccessor <IContainSagaData>(TransitionalCorrelationProperty); } }
public void DetectMessagesStartingTheSaga() { var metadata = SagaMetadata.Create(typeof(SagaWith2StartersAnd1Handler)); var messages = metadata.AssociatedMessages; Assert.AreEqual(4, messages.Count); Assert.True(metadata.IsMessageAllowedToStartTheSaga(typeof(SagaWith2StartersAnd1Handler.StartMessage1).FullName)); Assert.True(metadata.IsMessageAllowedToStartTheSaga(typeof(SagaWith2StartersAnd1Handler.StartMessage2).FullName)); Assert.False(metadata.IsMessageAllowedToStartTheSaga(typeof(SagaWith2StartersAnd1Handler.Message3).FullName)); Assert.False(metadata.IsMessageAllowedToStartTheSaga(typeof(SagaWith2StartersAnd1Handler.MyTimeout).FullName)); }
public static SqlSagaTypeData GetTypeData(SagaMetadata metadata) { var sagaType = metadata.SagaType; if (sagaType.IsSubclassOfRawGeneric(typeof(SqlSaga <>))) { return(GetTypeDataFromSqlSaga(sagaType)); } if (sagaType.IsSubclassOfRawGeneric(typeof(NServiceBus.Saga <>))) { return(GetTypeDataFromCoreSaga(metadata)); } throw new Exception($"Type '{sagaType.FullName}' is not a Saga<T>."); }
static bool IsMessageAllowedToStartTheSaga(IInvokeHandlerContext context, SagaMetadata sagaMetadata) { if (context.Headers.ContainsKey(Headers.SagaId) && context.Headers.TryGetValue(Headers.SagaType, out var sagaType)) { //we want to move away from the assembly fully qualified name since that will break if you move sagas //between assemblies. We use the FullName instead, which is enough to identify the saga. if (sagaType.StartsWith(sagaMetadata.Name)) { //so now we have a saga id for this saga, and if we can't find it, we shouldn't start a new one return(false); } } return(context.MessageMetadata.MessageHierarchy.Any(messageType => sagaMetadata.IsMessageAllowedToStartTheSaga(messageType.FullName))); }
static bool IsMessageAllowedToStartTheSaga(LogicalMessage message, SagaMetadata sagaMetadata) { string sagaType; if (message.Headers.ContainsKey(Headers.SagaId) && message.Headers.TryGetValue(Headers.SagaType, out sagaType)) { //we want to move away from the assembly fully qualified name since that will break if you move sagas // between assemblies. We use the fullname instead which is enough to identify the saga if (sagaType.StartsWith(sagaMetadata.Name)) { //so now we have a saga id for this saga and if we can't find it we shouldn't start a new one return(false); } } return(message.Metadata.MessageHierarchy.Any(messageType => sagaMetadata.IsMessageAllowedToStartTheSaga(messageType.FullName))); }
IContainSagaData CreateNewSagaEntity(SagaMetadata metadata, LogicalMessage message) { var sagaEntityType = metadata.SagaEntityType; var sagaEntity = (IContainSagaData)Activator.CreateInstance(sagaEntityType); sagaEntity.Id = CombGuid.Generate(); sagaEntity.OriginalMessageId = message.Headers[Headers.MessageId]; string replyToAddress; if (message.Headers.TryGetValue(Headers.ReplyToAddress, out replyToAddress)) { sagaEntity.Originator = replyToAddress; } return(sagaEntity); }
IContainSagaData CreateNewSagaEntity(SagaMetadata metadata, IInvokeHandlerContext context) { var sagaEntityType = metadata.SagaEntityType; var sagaEntity = (IContainSagaData)Activator.CreateInstance(sagaEntityType); sagaEntity.OriginalMessageId = context.MessageId; string replyToAddress; if (context.Headers.TryGetValue(Headers.ReplyToAddress, out replyToAddress)) { sagaEntity.Originator = replyToAddress; } var lookupValues = context.Extensions.GetOrCreate <SagaLookupValues>(); SagaCorrelationProperty correlationProperty; SagaLookupValues.LookupValue value; if (lookupValues.TryGet(sagaEntityType, out value)) { var propertyInfo = sagaEntityType.GetProperty(value.PropertyName); var convertedValue = TypeDescriptor.GetConverter(propertyInfo.PropertyType) .ConvertFromInvariantString(value.PropertyValue.ToString()); propertyInfo.SetValue(sagaEntity, convertedValue); correlationProperty = new SagaCorrelationProperty(value.PropertyName, value.PropertyValue); } else { correlationProperty = SagaCorrelationProperty.None; } var sagaIdGeneratorContext = new SagaIdGeneratorContext(correlationProperty, metadata, context.Extensions); sagaEntity.Id = sagaIdGenerator.Generate(sagaIdGeneratorContext); return(sagaEntity); }
public MetadataPipelineHookInitialisationTests() { currentTime = new DateTime(1905, 9, 13); TimeProvider.Current = new StubTimeProvider(currentTime); sut = new MetadataPipelineHook(new JsonNetSerialiser()); correlationId = Guid.NewGuid(); message = new MySagaInitiatingMessage(correlationId) { SomeRandomValue = Guid.NewGuid(), }; saga = new MySaga(); var context = new PipelineContext(message, saga); sut.AfterInitialisation(context); sagaMetadata = saga.GetSagaMetadata(new JsonNetSerialiser()); }
public MessagesHandler(HandlerFactories handlerFactories, HandlerFactories sagaHandlerFactories, SagaMetadata[] sagaMetadata) { this.handlerFactories = handlerFactories; this.sagaHandlerFactories = sagaHandlerFactories; sagaHandlersInfo = new Dictionary<Type, IDictionary<Type, SagaHandlerInfo>>(); foreach (var sagaMeta in sagaMetadata) { foreach (var sagaMessage in sagaMeta.AssociatedMessages) { IDictionary<Type, SagaHandlerInfo> sagaHandlersInfoForMessageType; if (!sagaHandlersInfo.TryGetValue(sagaMessage.MessageType, out sagaHandlersInfoForMessageType)) { sagaHandlersInfo[sagaMessage.MessageType] = sagaHandlersInfoForMessageType = new Dictionary<Type, SagaHandlerInfo>(); } sagaHandlersInfoForMessageType[sagaMeta.SagaDataType] = new SagaHandlerInfo(sagaMessage.SagaHandlerType, sagaMessage.IsAllowedToStartSaga); } } }
static SqlSagaTypeData GetTypeDataFromCoreSaga(SagaMetadata metadata) { var attribute = metadata.SagaType.GetCustomAttributes().OfType <SqlSagaAttribute>().FirstOrDefault(); var correlationProperty = attribute?.CorrelationProperty; if (correlationProperty == null) { if (metadata.TryGetCorrelationProperty(out var property)) { correlationProperty = property.Name; } } return(new SqlSagaTypeData { TableSuffix = attribute?.TableSuffix ?? metadata.SagaType.Name, CorrelationProperty = correlationProperty, TransitionalCorrelationProperty = attribute?.TransitionalCorrelationProperty }); }
IContainSagaData TryLoadSagaEntity(SagaMetadata metadata, LogicalMessage message) { string sagaId; if (message.Headers.TryGetValue(Headers.SagaId, out sagaId) && !string.IsNullOrEmpty(sagaId)) { var sagaEntityType = metadata.SagaEntityType; //since we have a saga id available we can now shortcut the finders and just load the saga var loaderType = typeof(LoadSagaByIdWrapper <>).MakeGenericType(sagaEntityType); var loader = (SagaLoader)Activator.CreateInstance(loaderType); return(loader.Load(SagaPersister, sagaId)); } SagaFinderDefinition finderDefinition = null; foreach (var messageType in message.Metadata.MessageHierarchy) { if (metadata.TryGetFinder(messageType.FullName, out finderDefinition)) { break; } } //check if we could find a finder if (finderDefinition == null) { return(null); } var finderType = finderDefinition.Type; var finder = currentContext.Builder.Build(finderType); return(((SagaFinder)finder).Find(currentContext.Builder, finderDefinition, message)); }
public void GetSagaClrType() { var metadata = SagaMetadata.Create(typeof(MySaga)); Assert.AreEqual(typeof(MySaga), metadata.SagaType); }
public void GetEntityClrTypeFromInheritanceChain() { var metadata = SagaMetadata.Create(typeof(SagaWithInheritanceChain)); Assert.AreEqual(typeof(SagaWithInheritanceChain.SagaData), metadata.SagaEntityType); }
public void GetEntityClrType() { var metadata = SagaMetadata.Create(typeof(MySaga)); Assert.AreEqual(typeof(MySaga.MyEntity), metadata.SagaEntityType); }
public void ValidateThrowsWhenSagaMapsMessageItDoesntHandle(Type sagaType) { var ex = Assert.Throws <Exception>(() => SagaMetadata.Create(sagaType)); Assert.That(ex.Message.Contains("does not handle that message") && ex.Message.Contains("in the ConfigureHowToFindSaga method")); }
public void ValidateThatSagaPropertyIsNotAField() { var ex = Assert.Throws <InvalidOperationException>(() => SagaMetadata.Create(typeof(SagaWithSagaDataMemberAsFieldInsteadOfProperty))); StringAssert.Contains(typeof(SagaWithSagaDataMemberAsFieldInsteadOfProperty.SagaData).FullName, ex.Message); }
public void ValidateThatMappingOnSagaIdHasTypeGuidForMessageFields() { var ex = Assert.Throws <InvalidOperationException>(() => SagaMetadata.Create(typeof(SagaWithIdMappedToNonGuidMessageField))); StringAssert.Contains(typeof(SomeMessage).Name, ex.Message); }
public void ValidateThatMappingOnNonSagaIdGuidPropertyFromStringToGuidForMessagePropsThrowsException() { var ex = Assert.Throws <InvalidOperationException>(() => SagaMetadata.Create(typeof(SagaWithNonIdPropertyMappedToStringMessageProperty))); StringAssert.Contains(typeof(SomeMessage).FullName, ex.Message); }
public void Throws_when_does_not_implement_generic_saga() { Assert.Throws <Exception>(() => SagaMetadata.Create(typeof(MyNonGenericSaga))); }
/// <summary> /// Initializes a new instance of <see cref="SagaEventHandler"/>. /// </summary> /// <param name="eventHandler">The base event handler to decorate.</param> /// <param name="sagaMetadata">The saga metadata associated with this saga event handler.</param> /// <param name="sagaStore">The saga store used to load/save saga state.</param> /// <param name="commandPublisher">The command publisher used to publish saga commands.</param> internal SagaEventHandler(EventHandler eventHandler, SagaMetadata sagaMetadata, IStoreSagas sagaStore, Lazy<IPublishCommands> commandPublisher) : this(eventHandler, sagaMetadata, sagaStore, commandPublisher, Settings.SagaStore) { }
public void HandleNonExistingFinders() { var ex = Assert.Throws <Exception>(() => SagaMetadata.Create(typeof(MySagaWithUnmappedStartProperty))); Assert.That(ex.Message.Contains("mapper.ConfigureMapping")); }