protected UsingInitializedSagaStore()
 {
     SagaId      = GuidStrategy.NewGuid();
     SagaContext = new SagaContext(typeof(FakeSaga), SagaId, new FakeEvent());
     TypeLocator.Setup(mock => mock.GetTypes(It.IsAny <Func <Type, Boolean> >())).Returns(new[] { typeof(FakeSaga) });
     SagaStore = new SqlSagaStore(Dialect, Serializer, TypeLocator.Object);
     SagaStore.Purge();
 }
        /// <summary>
        /// Initializes a new instance of <see cref="SagaTimeoutCache"/>.
        /// </summary>
        /// <param name="sagaStore">The saga store used to retrieve pending saga timeouts.</param>
        /// <param name="timeoutCacheDuration">The maximum cache duration for a given saga timeout (5 minute minimum).</param>
        public SagaTimeoutCache(IStoreSagas sagaStore, TimeSpan timeoutCacheDuration)
        {
            Verify.NotNull(sagaStore, nameof(sagaStore));

            this.sagaStore = sagaStore;
            this.maximumCachedTimeout = DateTime.MinValue;
            this.timeoutCacheDuration = timeoutCacheDuration < MinimumCacheDuration ? MinimumCacheDuration : timeoutCacheDuration;
        }
Beispiel #3
0
        /// <summary>
        /// Initializes a new instance of <see cref="SagaTimeoutCache"/>.
        /// </summary>
        /// <param name="sagaStore">The saga store used to retrieve pending saga timeouts.</param>
        /// <param name="timeoutCacheDuration">The maximum cache duration for a given saga timeout (5 minute minimum).</param>
        public SagaTimeoutCache(IStoreSagas sagaStore, TimeSpan timeoutCacheDuration)
        {
            Verify.NotNull(sagaStore, nameof(sagaStore));

            this.sagaStore            = sagaStore;
            this.maximumCachedTimeout = DateTime.MinValue;
            this.timeoutCacheDuration = timeoutCacheDuration < MinimumCacheDuration ? MinimumCacheDuration : timeoutCacheDuration;
        }
 protected UsingInitializedSagaStore()
 {
     SagaId = GuidStrategy.NewGuid();
     SagaContext = new SagaContext(typeof(FakeSaga), SagaId, new FakeEvent());
     TypeLocator.Setup(mock => mock.GetTypes(It.IsAny<Func<Type, Boolean>>())).Returns(new[] { typeof(FakeSaga) });
     SagaStore = new SqlSagaStore(Dialect, Serializer, TypeLocator.Object);
     SagaStore.Purge();
 }
        /// <summary>
        /// Initializes a new instance of <see cref="HookableSagaStore"/> with <paramref name="pipelineHooks"/> safe to enumerate multiple times.
        /// </summary>
        /// <param name="sagaStore">The underlying <see cref="IStoreSagas"/> implementation to be decorated.</param>
        /// <param name="pipelineHooks">The set of zero or more <see cref="PipelineHook"/> implementations used to extend <see cref="IStoreSagas"/> behavior.</param>
        private HookableSagaStore(IStoreSagas sagaStore, IList<PipelineHook> pipelineHooks)
        {
            Verify.NotNull(sagaStore, nameof(sagaStore));

            this.sagaStore = sagaStore;
            this.preGetHooks = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPreGet).ToArray();
            this.postGetHooks = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPostGet).Reverse().ToArray();
            this.preSaveHooks = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPreSave).ToArray();
            this.postSaveHooks = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPostSave).Reverse().ToArray();
        }
Beispiel #6
0
        /// <summary>
        /// Initializes a new instance of <see cref="HookableSagaStore"/> with <paramref name="pipelineHooks"/> safe to enumerate multiple times.
        /// </summary>
        /// <param name="sagaStore">The underlying <see cref="IStoreSagas"/> implementation to be decorated.</param>
        /// <param name="pipelineHooks">The set of zero or more <see cref="PipelineHook"/> implementations used to extend <see cref="IStoreSagas"/> behavior.</param>
        private HookableSagaStore(IStoreSagas sagaStore, IList <PipelineHook> pipelineHooks)
        {
            Verify.NotNull(sagaStore, nameof(sagaStore));

            this.sagaStore     = sagaStore;
            this.preGetHooks   = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPreGet).ToArray();
            this.postGetHooks  = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPostGet).Reverse().ToArray();
            this.preSaveHooks  = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPreSave).ToArray();
            this.postSaveHooks = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPostSave).Reverse().ToArray();
        }
Beispiel #7
0
        /// <summary>
        /// Initializes a new instance of <see cref="CachedSagaStore"/> using the specified <paramref name="slidingExpiration"/>.
        /// </summary>
        /// <param name="sagaStore">The underlying <see cref="IStoreSagas"/> implementation to be decorated.</param>
        /// <param name="slidingExpiration">The maximum time an <see cref="Saga"/> may existing in the cache without being accessed.</param>
        /// <param name="memoryCache">The underlying cache implementation.</param>
        internal CachedSagaStore(IStoreSagas sagaStore, TimeSpan slidingExpiration, MemoryCache memoryCache)
        {
            Verify.NotNull(sagaStore, nameof(sagaStore));
            Verify.NotNull(memoryCache, nameof(memoryCache));
            Verify.GreaterThanOrEqual(TimeSpan.FromSeconds(1), slidingExpiration, nameof(sagaStore));

            this.sagaStore         = sagaStore;
            this.memoryCache       = memoryCache;
            this.slidingExpiration = slidingExpiration;
        }
        /// <summary>
        /// Initializes a new instance of <see cref="CachedSagaStore"/> using the specified <paramref name="slidingExpiration"/>.
        /// </summary>
        /// <param name="sagaStore">The underlying <see cref="IStoreSagas"/> implementation to be decorated.</param>
        /// <param name="slidingExpiration">The maximum time an <see cref="Saga"/> may existing in the cache without being accessed.</param>
        /// <param name="memoryCache">The underlying cache implementation.</param>
        internal CachedSagaStore(IStoreSagas sagaStore, TimeSpan slidingExpiration, MemoryCache memoryCache)
        {
            Verify.NotNull(sagaStore, nameof(sagaStore));
            Verify.NotNull(memoryCache, nameof(memoryCache));
            Verify.GreaterThanOrEqual(TimeSpan.FromSeconds(1), slidingExpiration, nameof(sagaStore));

            this.sagaStore = sagaStore;
            this.memoryCache = memoryCache;
            this.slidingExpiration = slidingExpiration;
        }
        /// <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;
        }
        /// <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;
        }
        /// <summary>
        /// Initializes a new instance of <see cref="EventHandlerRegistry"/> with the specified <paramref name="typeLocator"/> and <paramref name="serviceProvider"/>.
        /// </summary>
        /// <param name="sagaStore">The saga store to pass on to any <see cref="SagaEventHandler"/> instances.</param>
        /// <param name="typeLocator">The type locator used to retrieve all known <see cref="Event"/> types.</param>
        /// <param name="serviceProvider">The service locator used to retrieve singleton event handler dependencies.</param>
        /// <param name="commandPublisher">The command publisher used to publish saga commands.</param>
        public EventHandlerRegistry(ILocateTypes typeLocator, IServiceProvider serviceProvider, IStoreSagas sagaStore, Lazy<IPublishCommands> commandPublisher)
        {
            Verify.NotNull(sagaStore, nameof(sagaStore));
            Verify.NotNull(typeLocator, nameof(typeLocator));
            Verify.NotNull(serviceProvider, nameof(serviceProvider));
            Verify.NotNull(commandPublisher, nameof(commandPublisher));

            knownEventHandlers = DiscoverEventHandlers(typeLocator, serviceProvider, sagaStore, commandPublisher);
            knownSagaTimeoutHandlers = knownEventHandlers.Where(item => typeof(Timeout).IsAssignableFrom(item.Key))
                                                         .SelectMany(item => item.Value)
                                                         .OfType<SagaEventHandler>()
                                                         .Distinct(item => item.HandlerType)
                                                         .ToDictionary(item => item.HandlerType, item => new EventHandler[] { item });
        }
Beispiel #12
0
        /// <summary>
        /// Discover all event handlers methods associated with any locatable class marked with with <see cref="EventHandlerAttribute"/>.
        /// </summary>
        /// <param name="sagaStore">The saga store to pass on to any <see cref="SagaEventHandler"/> instances.</param>
        /// <param name="typeLocator">The type locator use to retrieve all known classes marked with <see cref="EventHandlerAttribute"/>.</param>
        /// <param name="serviceProvider">The service locator used to retrieve singleton event handler dependencies.</param>
        /// <param name="commandPublisher">The command publisher used to publish saga commands.</param>
        private static Dictionary <Type, List <EventHandler> > DiscoverHandleMethods(ILocateTypes typeLocator, IServiceProvider serviceProvider, IStoreSagas sagaStore, Lazy <IPublishCommands> commandPublisher)
        {
            var handlerTypes       = typeLocator.GetTypes(type => !type.IsAbstract && type.IsClass && type.GetCustomAttribute <EventHandlerAttribute>() != null);
            var knownEventHandlers = new Dictionary <Type, List <EventHandler> >();

            foreach (var handlerType in handlerTypes)
            {
                var handleMethods = GetHandleMethods(handlerType, serviceProvider);
                var sagaMetadata  = typeof(Saga).IsAssignableFrom(handlerType) ? GetSagaMetadata(handlerType, handleMethods) : null;

                foreach (var handleMethod in handleMethods)
                {
                    List <EventHandler> eventHandlers;
                    Type eventType = handleMethod.Key;
                    if (!knownEventHandlers.TryGetValue(eventType, out eventHandlers))
                    {
                        knownEventHandlers.Add(eventType, eventHandlers = new List <EventHandler>());
                    }

                    var eventHandler = new EventHandler(handlerType, eventType, handleMethod.Value, GetHandlerFactory(handlerType, serviceProvider));
                    if (sagaMetadata != null)
                    {
                        eventHandler = eventType == typeof(Timeout) ? new SagaTimeoutHandler(eventHandler, sagaMetadata, sagaStore, commandPublisher) : new SagaEventHandler(eventHandler, sagaMetadata, sagaStore, commandPublisher);
                    }

                    eventHandlers.Add(eventHandler);
                }
            }

            return(knownEventHandlers);
        }
 /// <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)
 {
 }
 /// <summary>
 /// Initalizes a new isntance of <see cref="BenchmarkedSagaStore"/>.
 /// </summary>
 /// <param name="sagaStore">The saga store to decorate.</param>
 /// <param name="statistics">The statistics class.</param>
 public BenchmarkedSagaStore(IStoreSagas sagaStore, Statistics statistics)
 {
     this.sagaStore = sagaStore;
     this.statistics = statistics;
 }
Beispiel #15
0
 /// <summary>
 /// Initializes a new instance of <see cref="CachedSagaStore"/>.
 /// </summary>
 /// <param name="sagaStore">The underlying <see cref="IStoreSagas"/> implementation to be decorated.</param>
 public CachedSagaStore(IStoreSagas sagaStore)
     : this(sagaStore, Settings.SagaStore.CacheSlidingExpiration, new MemoryCache("SagaCache"))
 {
 }
        /// <summary>
        /// Discover all event handlers associated with any locatable class marked with <see cref="EventHandlerAttribute"/>.
        /// </summary>
        /// <param name="sagaStore">The saga store to pass on to any <see cref="SagaEventHandler"/> instances.</param>
        /// <param name="typeLocator">The type locator use to retrieve all known classes marked with <see cref="EventHandlerAttribute"/>.</param>
        /// <param name="serviceProvider">The service locator used to retrieve singleton event handler dependencies.</param>
        /// <param name="commandPublisher">The command publisher used to publish saga commands.</param>
        private static Dictionary<Type, EventHandler[]> DiscoverEventHandlers(ILocateTypes typeLocator, IServiceProvider serviceProvider, IStoreSagas sagaStore, Lazy<IPublishCommands> commandPublisher)
        {
            var knownEvents = typeLocator.GetTypes(type => !type.IsAbstract && type.IsClass && type.DerivesFrom(typeof(Event)));
            var knownHandlers = DiscoverHandleMethods(typeLocator, serviceProvider, sagaStore, commandPublisher);
            var result = new Dictionary<Type, EventHandler[]>();
            var logMessage = new StringBuilder();

            logMessage.AppendLine("Discovered event handlers:");
            foreach (var eventType in knownEvents.OrderBy(type => type.FullName))
            {
                var eventHandlers = eventType.GetTypeHierarchy().Reverse()
                                             .Where(knownHandlers.ContainsKey)
                                             .SelectMany(type => knownHandlers[type])
                                             .OrderBy(handler => handler is SagaEventHandler)
                                             .ThenBy(handler => handler.HandlerType.AssemblyQualifiedName)
                                             .ToArray();

                logMessage.Append("    ");
                logMessage.Append(eventType);
                logMessage.AppendLine();

                foreach (var eventHandler in eventHandlers)
                {
                    logMessage.Append("        ");
                    logMessage.Append(eventHandler.HandlerType);
                    logMessage.AppendLine();
                }

                result.Add(eventType, eventHandlers);
            }

            Log.Debug(logMessage.ToString);

            return result;
        }
        /// <summary>
        /// Discover all event handlers methods associated with any locatable class marked with with <see cref="EventHandlerAttribute"/>.
        /// </summary>
        /// <param name="sagaStore">The saga store to pass on to any <see cref="SagaEventHandler"/> instances.</param>
        /// <param name="typeLocator">The type locator use to retrieve all known classes marked with <see cref="EventHandlerAttribute"/>.</param>
        /// <param name="serviceProvider">The service locator used to retrieve singleton event handler dependencies.</param>
        /// <param name="commandPublisher">The command publisher used to publish saga commands.</param>
        private static Dictionary<Type, List<EventHandler>> DiscoverHandleMethods(ILocateTypes typeLocator, IServiceProvider serviceProvider, IStoreSagas sagaStore, Lazy<IPublishCommands> commandPublisher)
        {
            var handlerTypes = typeLocator.GetTypes(type => !type.IsAbstract && type.IsClass && type.GetCustomAttribute<EventHandlerAttribute>() != null);
            var knownEventHandlers = new Dictionary<Type, List<EventHandler>>();

            foreach (var handlerType in handlerTypes)
            {
                var handleMethods = GetHandleMethods(handlerType, serviceProvider);
                var sagaMetadata = typeof(Saga).IsAssignableFrom(handlerType) ? GetSagaMetadata(handlerType, handleMethods) : null;

                foreach (var handleMethod in handleMethods)
                {
                    List<EventHandler> eventHandlers;
                    Type eventType = handleMethod.Key;
                    if (!knownEventHandlers.TryGetValue(eventType, out eventHandlers))
                        knownEventHandlers.Add(eventType, eventHandlers = new List<EventHandler>());

                    var eventHandler = new EventHandler(handlerType, eventType, handleMethod.Value, GetHandlerFactory(handlerType, serviceProvider));
                    if (sagaMetadata != null)
                        eventHandler = eventType == typeof(Timeout) ? new SagaTimeoutHandler(eventHandler, sagaMetadata, sagaStore, commandPublisher) : new SagaEventHandler(eventHandler, sagaMetadata, sagaStore, commandPublisher);

                    eventHandlers.Add(eventHandler);
                }
            }

            return knownEventHandlers;
        }
Beispiel #18
0
 /// <summary>
 /// Initalizes a new isntance of <see cref="BenchmarkedSagaStore"/>.
 /// </summary>
 /// <param name="sagaStore">The saga store to decorate.</param>
 /// <param name="statistics">The statistics class.</param>
 public BenchmarkedSagaStore(IStoreSagas sagaStore, Statistics statistics)
 {
     this.sagaStore  = sagaStore;
     this.statistics = statistics;
 }
Beispiel #19
0
        /// <summary>
        /// Discover all event handlers associated with any locatable class marked with <see cref="EventHandlerAttribute"/>.
        /// </summary>
        /// <param name="sagaStore">The saga store to pass on to any <see cref="SagaEventHandler"/> instances.</param>
        /// <param name="typeLocator">The type locator use to retrieve all known classes marked with <see cref="EventHandlerAttribute"/>.</param>
        /// <param name="serviceProvider">The service locator used to retrieve singleton event handler dependencies.</param>
        /// <param name="commandPublisher">The command publisher used to publish saga commands.</param>
        private static Dictionary <Type, EventHandler[]> DiscoverEventHandlers(ILocateTypes typeLocator, IServiceProvider serviceProvider, IStoreSagas sagaStore, Lazy <IPublishCommands> commandPublisher)
        {
            var knownEvents   = typeLocator.GetTypes(type => !type.IsAbstract && type.IsClass && type.DerivesFrom(typeof(Event)));
            var knownHandlers = DiscoverHandleMethods(typeLocator, serviceProvider, sagaStore, commandPublisher);
            var result        = new Dictionary <Type, EventHandler[]>();
            var logMessage    = new StringBuilder();

            logMessage.AppendLine("Discovered event handlers:");
            foreach (var eventType in knownEvents.OrderBy(type => type.FullName))
            {
                var eventHandlers = eventType.GetTypeHierarchy().Reverse()
                                    .Where(knownHandlers.ContainsKey)
                                    .SelectMany(type => knownHandlers[type])
                                    .OrderBy(handler => handler is SagaEventHandler)
                                    .ThenBy(handler => handler.HandlerType.AssemblyQualifiedName)
                                    .ToArray();

                logMessage.Append("    ");
                logMessage.Append(eventType);
                logMessage.AppendLine();

                foreach (var eventHandler in eventHandlers)
                {
                    logMessage.Append("        ");
                    logMessage.Append(eventHandler.HandlerType);
                    logMessage.AppendLine();
                }

                result.Add(eventType, eventHandlers);
            }

            Log.Debug(logMessage.ToString);

            return(result);
        }
 /// <summary>
 /// Initializes a new instance of <see cref="HookableSagaStore"/>.
 /// </summary>
 /// <param name="sagaStore">The underlying <see cref="IStoreSagas"/> implementation to be decorated.</param>
 /// <param name="pipelineHooks">The set of zero or more <see cref="PipelineHook"/> implementations used to extend <see cref="IStoreSagas"/> behavior.</param>
 public HookableSagaStore(IStoreSagas sagaStore, IEnumerable<PipelineHook> pipelineHooks)
     : this(sagaStore, pipelineHooks.EmptyIfNull().OrderBy(hook => hook.Order).ThenBy(hook => hook.GetType().FullName).ToList())
 {
 }
 /// <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)
 {
 }
 /// <summary>
 /// Initializes a new instance of <see cref="CachedSagaStore"/>.
 /// </summary>
 /// <param name="sagaStore">The underlying <see cref="IStoreSagas"/> implementation to be decorated.</param>
 public CachedSagaStore(IStoreSagas sagaStore)
     : this(sagaStore, Settings.SagaStore.CacheSlidingExpiration, new MemoryCache("SagaCache"))
 {
 }
Beispiel #23
0
        /// <summary>
        /// Initializes a new instance of <see cref="EventHandlerRegistry"/> with the specified <paramref name="typeLocator"/> and <paramref name="serviceProvider"/>.
        /// </summary>
        /// <param name="sagaStore">The saga store to pass on to any <see cref="SagaEventHandler"/> instances.</param>
        /// <param name="typeLocator">The type locator used to retrieve all known <see cref="Event"/> types.</param>
        /// <param name="serviceProvider">The service locator used to retrieve singleton event handler dependencies.</param>
        /// <param name="commandPublisher">The command publisher used to publish saga commands.</param>
        public EventHandlerRegistry(ILocateTypes typeLocator, IServiceProvider serviceProvider, IStoreSagas sagaStore, Lazy <IPublishCommands> commandPublisher)
        {
            Verify.NotNull(sagaStore, nameof(sagaStore));
            Verify.NotNull(typeLocator, nameof(typeLocator));
            Verify.NotNull(serviceProvider, nameof(serviceProvider));
            Verify.NotNull(commandPublisher, nameof(commandPublisher));

            knownEventHandlers       = DiscoverEventHandlers(typeLocator, serviceProvider, sagaStore, commandPublisher);
            knownSagaTimeoutHandlers = knownEventHandlers.Where(item => typeof(Timeout).IsAssignableFrom(item.Key))
                                       .SelectMany(item => item.Value)
                                       .OfType <SagaEventHandler>()
                                       .Distinct(item => item.HandlerType)
                                       .ToDictionary(item => item.HandlerType, item => new EventHandler[] { item });
        }
Beispiel #24
0
 /// <summary>
 /// Initializes a new instance of <see cref="HookableSagaStore"/>.
 /// </summary>
 /// <param name="sagaStore">The underlying <see cref="IStoreSagas"/> implementation to be decorated.</param>
 /// <param name="pipelineHooks">The set of zero or more <see cref="PipelineHook"/> implementations used to extend <see cref="IStoreSagas"/> behavior.</param>
 public HookableSagaStore(IStoreSagas sagaStore, IEnumerable <PipelineHook> pipelineHooks)
     : this(sagaStore, pipelineHooks.EmptyIfNull().OrderBy(hook => hook.Order).ThenBy(hook => hook.GetType().FullName).ToList())
 {
 }