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);
        }
Пример #3
0
        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);
        }
Пример #5
0
        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"]);
        }
Пример #6
0
 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));
            }
Пример #9
0
        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);
            }
        }
Пример #10
0
        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);
        }
    }
Пример #16
0
        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));
        }
Пример #17
0
    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>.");
    }
Пример #18
0
        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());
        }
Пример #23
0
        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);
                }
            }
        }
Пример #24
0
    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));
        }
Пример #26
0
        public void GetSagaClrType()
        {
            var metadata = SagaMetadata.Create(typeof(MySaga));

            Assert.AreEqual(typeof(MySaga), metadata.SagaType);
        }
Пример #27
0
        public void GetEntityClrTypeFromInheritanceChain()
        {
            var metadata = SagaMetadata.Create(typeof(SagaWithInheritanceChain));

            Assert.AreEqual(typeof(SagaWithInheritanceChain.SagaData), metadata.SagaEntityType);
        }
Пример #28
0
        public void GetEntityClrType()
        {
            var metadata = SagaMetadata.Create(typeof(MySaga));

            Assert.AreEqual(typeof(MySaga.MyEntity), metadata.SagaEntityType);
        }
Пример #29
0
        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"));
        }
Пример #30
0
        public void ValidateThatSagaPropertyIsNotAField()
        {
            var ex = Assert.Throws <InvalidOperationException>(() => SagaMetadata.Create(typeof(SagaWithSagaDataMemberAsFieldInsteadOfProperty)));

            StringAssert.Contains(typeof(SagaWithSagaDataMemberAsFieldInsteadOfProperty.SagaData).FullName, ex.Message);
        }
Пример #31
0
        public void ValidateThatMappingOnSagaIdHasTypeGuidForMessageFields()
        {
            var ex = Assert.Throws <InvalidOperationException>(() => SagaMetadata.Create(typeof(SagaWithIdMappedToNonGuidMessageField)));

            StringAssert.Contains(typeof(SomeMessage).Name, ex.Message);
        }
Пример #32
0
        public void ValidateThatMappingOnNonSagaIdGuidPropertyFromStringToGuidForMessagePropsThrowsException()
        {
            var ex = Assert.Throws <InvalidOperationException>(() => SagaMetadata.Create(typeof(SagaWithNonIdPropertyMappedToStringMessageProperty)));

            StringAssert.Contains(typeof(SomeMessage).FullName, ex.Message);
        }
Пример #33
0
 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)
 {
 }
Пример #35
0
        public void HandleNonExistingFinders()
        {
            var ex = Assert.Throws <Exception>(() => SagaMetadata.Create(typeof(MySagaWithUnmappedStartProperty)));

            Assert.That(ex.Message.Contains("mapper.ConfigureMapping"));
        }