/// <summary>
        ///  When overriden, defines the custom behavior to be invoked after attempting to save the specified <paramref name="saga"/>.
        /// </summary>
        /// <param name="saga">The modified <see cref="Saga"/> instance if <paramref name="error"/> is <value>null</value>; otherwise the original <see cref="Saga"/> instance if <paramref name="error"/> is not <value>null</value>.</param>
        /// <param name="context">The current <see cref="SagaContext"/> assocaited with the pending saga modifications.</param>
        /// <param name="error">The <see cref="Exception"/> thrown if the save was unsuccessful; otherwise <value>null</value>.</param>
        public override void PostSave(Saga saga, SagaContext context, Exception error)
        {
            base.PostSave(saga, context, error);

            if (error is ConcurrencyException)
                statistics.IncrementConflictCount();
        }
        /// <summary>
        /// Initalizes a new instance of <see cref="SagaContext"/> with the specified <paramref name="sagaType"/> and <paramref name="sagaId"/>.
        /// </summary>
        /// <param name="sagaType">The underlying saga <see cref="Type"/> associated with this <see cref="SagaContext"/>.</param>
        /// <param name="sagaId">The <see cref="Saga"/> correlation id associated with this <see cref="SagaContext"/>.</param>
        /// <param name="e">The <see cref="Event"/>.</param>
        public SagaContext(Type sagaType, Guid sagaId, Event e)
        {
            Verify.NotNull(sagaType, nameof(sagaType));
            Verify.NotNull(e, nameof(e));

            this.originalContext = currentContext;
            this.thread = Thread.CurrentThread;
            this.sagaType = sagaType;
            this.sagaId = sagaId;
            this.@event = e;

            currentContext = this;
        }
        /// <summary>
        /// Invokes the underlying <see cref="Saga"/> event handler method using the specified event <paramref name="context"/>.
        /// </summary>
        /// <param name="context">The current event context.</param>
        public override void Handle(EventContext context)
        {
            Verify.NotNull(context, nameof(context));

            var sagaId = sagaMetadata.GetCorrelationId(context.Event);
            using (var sagaContext = new SagaContext(HandlerType, sagaId, context.Event))
            {
                Handle(sagaContext);

                var commandPublisher = lazyCommandPublisher.Value;
                foreach (var sagaCommand in sagaContext.GetPublishedCommands())
                    commandPublisher.Publish(sagaCommand.AggregateId, sagaCommand.Command, sagaCommand.Headers);
            }
        }
        /// <summary>
        /// Invokes the underlying <see cref="Saga"/> event handler method using the specified event <paramref name="context"/>.
        /// </summary>
        /// <param name="context">The current event context.</param>
        public override void Handle(EventContext context)
        {
            Verify.NotNull(context, nameof(context));

            var sagaId = sagaMetadata.GetCorrelationId(context.Event);

            using (var sagaContext = new SagaContext(HandlerType, sagaId, context.Event))
            {
                Handle(sagaContext);

                var commandPublisher = lazyCommandPublisher.Value;
                foreach (var sagaCommand in sagaContext.GetPublishedCommands())
                {
                    commandPublisher.Publish(sagaCommand.AggregateId, sagaCommand.Command, sagaCommand.Headers);
                }
            }
        }
Beispiel #5
0
        /// <summary>
        /// Save the specified <paramref name="context"/> changes for the given <paramref name="saga"/>.
        /// </summary>
        /// <param name="saga">The current saga version for which the context applies.</param>
        /// <param name="context">The saga context containing the saga changes to be applied.</param>
        public Saga Save(Saga saga, SagaContext context)
        {
            InvokePreSaveHooks(saga, context);

            try
            {
                var result = sagaStore.Save(saga, context);

                InvokePostSaveHooks(result, context, null);

                return(result);
            }
            catch (Exception ex)
            {
                InvokePostSaveHooks(saga, context, ex);
                throw;
            }
        }
        /// <summary>
        /// Invokes the underlying <see cref="Saga"/> event handler method using the current saga <paramref name="context"/>.
        /// </summary>
        /// <param name="context">The current saga context.</param>
        private void Handle(SagaContext context)
        {
            var e        = context.Event;
            var sagaId   = context.SagaId;
            var sagaType = context.SagaType;
            var saga     = GetOrCreateSaga(sagaType, sagaId, e);

            if (saga != null)
            {
                HandleSagaEvent(saga, e);

                sagaStore.Save(saga, context);
            }
            else
            {
                Log.Trace("{0} - {1} cannot be initiated by event {2}", sagaType, sagaId, context.Event);
            }
        }
        /// <summary>
        ///  When overriden, defines the custom behavior to be invoked after attempting to save the specified <paramref name="saga"/>.
        /// </summary>
        /// <param name="saga">The modified <see cref="Saga"/> instance if <paramref name="error"/> is <value>null</value>; otherwise the original <see cref="Saga"/> instance if <paramref name="error"/> is not <value>null</value>.</param>
        /// <param name="context">The current <see cref="SagaContext"/> assocaited with the pending saga modifications.</param>
        /// <param name="error">The <see cref="Exception"/> thrown if the save was unsuccessful; otherwise <value>null</value>.</param>
        public override void PostSave(Saga saga, SagaContext context, Exception error)
        {
            if (!context.TimeoutChanged || saga == null || error != null)
            {
                return;
            }

            if (saga.Timeout.HasValue && !saga.Completed)
            {
                var timeout = saga.Timeout.Value;

                TimeoutCache.ScheduleTimeout(new SagaTimeout(saga.GetType(), saga.CorrelationId, timeout));
            }
            else
            {
                TimeoutCache.ClearTimeout(new SagaReference(saga.GetType(), saga.CorrelationId));
            }

            RescheduleTimer(TimeoutCache.GetNextScheduledTimeout());
        }
Beispiel #8
0
        /// <summary>
        /// Releases all managed resources used by the current instance of the <see cref="SagaContext"/> class.
        /// </summary>
        public void Dispose()
        {
            if (disposed)
            {
                return;
            }

            if (this.thread != Thread.CurrentThread)
            {
                throw new InvalidOperationException(Exceptions.SagaContextInterleaved);
            }

            if (this != Current)
            {
                throw new InvalidOperationException(Exceptions.SagaContextInvalidThread);
            }

            disposed       = true;
            currentContext = originalContext;
        }
 /// <summary>
 /// Invokes zero or more customized <see cref="PipelineHook.PreSave"/> implementations.
 /// </summary>
 /// <param name="saga">The saga to be modified by the current <paramref name="context"/>.</param>
 /// <param name="context">The current <see cref="SagaContext"/> associated with the pending saga modifications.</param>
 private void InvokePreSaveHooks(Saga saga, SagaContext context)
 {
     foreach (var pipelineHook in preSaveHooks)
     {
         Log.Trace("Invoking pre-save pipeline hook: {0}", pipelineHook);
         pipelineHook.PreSave(saga, context);
     }
 }
        /// <summary>
        /// Releases all managed resources used by the current instance of the <see cref="SagaContext"/> class.
        /// </summary>
        public void Dispose()
        {
            if (disposed)
                return;

            if (this.thread != Thread.CurrentThread)
                throw new InvalidOperationException(Exceptions.SagaContextInterleaved);

            if (this != Current)
                throw new InvalidOperationException(Exceptions.SagaContextInvalidThread);

            disposed = true;
            currentContext = originalContext;
        }
        /// <summary>
        ///  When overriden, defines the custom behavior to be invoked after attempting to save the specified <paramref name="saga"/>.
        /// </summary>
        /// <param name="saga">The modified <see cref="Saga"/> instance if <paramref name="error"/> is <value>null</value>; otherwise the original <see cref="Saga"/> instance if <paramref name="error"/> is not <value>null</value>.</param>
        /// <param name="context">The current <see cref="SagaContext"/> assocaited with the pending saga modifications.</param>
        /// <param name="error">The <see cref="Exception"/> thrown if the save was unsuccessful; otherwise <value>null</value>.</param>
        public override void PostSave(Saga saga, SagaContext context, Exception error)
        {
            if (!context.TimeoutChanged || saga == null || error != null)
                return;

            if (saga.Timeout.HasValue && !saga.Completed)
            {
                var timeout = saga.Timeout.Value;

                TimeoutCache.ScheduleTimeout(new SagaTimeout(saga.GetType(), saga.CorrelationId, timeout));
            }
            else
            {
                TimeoutCache.ClearTimeout(new SagaReference(saga.GetType(), saga.CorrelationId));
            }

            RescheduleTimer(TimeoutCache.GetNextScheduledTimeout());
        }
Beispiel #12
0
 /// <summary>
 ///  When overriden, defines the custom behavior to be invoked after attempting to save the specified <paramref name="saga"/>.
 /// </summary>
 /// <param name="saga">The modified <see cref="Saga"/> instance if <paramref name="error"/> is <value>null</value>; otherwise the original <see cref="Saga"/> instance if <paramref name="error"/> is not <value>null</value>.</param>
 /// <param name="context">The current <see cref="SagaContext"/> assocaited with the pending saga modifications.</param>
 /// <param name="error">The <see cref="Exception"/> thrown if the save was unsuccessful; otherwise <value>null</value>.</param>
 public virtual void PostSave(Saga saga, SagaContext context, Exception error)
 {
 }
        /// <summary>
        /// Invokes the underlying <see cref="Saga"/> event handler method using the current saga <paramref name="context"/>.
        /// </summary>
        /// <param name="context">The current saga context.</param>
        private void Handle(SagaContext context)
        {
            var e = context.Event;
            var sagaId = context.SagaId;
            var sagaType = context.SagaType;
            var saga = GetOrCreateSaga(sagaType, sagaId, e);

            if (saga != null)
            {
                HandleSagaEvent(saga, e);

                sagaStore.Save(saga, context);
            }
            else
            {
                Log.Trace("{0} - {1} cannot be initiated by event {2}", sagaType, sagaId, context.Event);
            }
        }
        /// <summary>
        /// Save the specified <paramref name="context"/> changes for the given <paramref name="saga"/>.
        /// </summary>
        /// <param name="saga">The current saga version for which the context applies.</param>
        /// <param name="context">The saga context containing the saga changes to be applied.</param>
        public Saga Save(Saga saga, SagaContext context)
        {
            Verify.NotNull(saga, nameof(saga));
            Verify.NotNull(context, nameof(context));

            var sagaType = saga.GetType();
            var key = String.Concat(sagaType.GetFullNameWithAssembly(), "-", saga.CorrelationId);
            using (var sagaLock = new SagaLock(sagaType, saga.CorrelationId))
            {
                sagaLock.Aquire();

                try
                {
                    sagaStore.Save(saga, context);

                    if (saga.Completed)
                        memoryCache.Remove(key);
                    else
                        memoryCache.Set(key, saga, CreateCacheItemPolicy());

                    return saga;
                }
                catch (ConcurrencyException)
                {
                    memoryCache.Remove(key);
                    throw;
                }
            }
        }
Beispiel #15
0
 /// <summary>
 /// When overriden, defines the custom behavior to be invoked prior to saving the specified <paramref name="saga"/>.
 /// </summary>
 /// <param name="saga">The saga to be modified using the current <paramref name="context"/>.</param>
 /// <param name="context">The current <see cref="SagaContext"/> assocaited with the pending saga modifications.</param>
 public virtual void PreSave(Saga saga, SagaContext context)
 {
 }
 /// <summary>
 /// Invokes zero or more customized <see cref="PipelineHook.PostSave"/> implementations.
 /// </summary>
 /// <param name="saga">The modified <see cref="Saga"/> instance if <paramref name="error"/> is <value>null</value>; otherwise the original <see cref="Saga"/> instance if <paramref name="error"/> is not <value>null</value>.</param>
 /// <param name="context">The current <see cref="SagaContext"/> assocaited with the pending saga modifications.</param>
 /// <param name="error">The <see cref="Exception"/> thrown if the save was unsuccessful; otherwise <value>null</value>.</param>
 private void InvokePostSaveHooks(Saga saga, SagaContext context, Exception error)
 {
     foreach (var pipelineHook in postSaveHooks)
     {
         Log.Trace("Invoking post-save pipeline hook: {0}", pipelineHook);
         pipelineHook.PostSave(saga, context, error);
     }
 }
        /// <summary>
        /// Save the specified <paramref name="context"/> changes for the given <paramref name="saga"/>.
        /// </summary>
        /// <param name="saga">The current saga version for which the context applies.</param>
        /// <param name="context">The saga context containing the saga changes to be applied.</param>
        public Saga Save(Saga saga, SagaContext context)
        {
            InvokePreSaveHooks(saga, context);

            try
            {
                var result = sagaStore.Save(saga, context);

                InvokePostSaveHooks(result, context, null);

                return result;
            }
            catch (Exception ex)
            {
                InvokePostSaveHooks(saga, context, ex);
                throw;
            }
        }
 /// <summary>
 /// When overriden, defines the custom behavior to be invoked prior to saving the specified <paramref name="saga"/>.
 /// </summary>
 /// <param name="saga">The saga to be modified using the current <paramref name="context"/>.</param>
 /// <param name="context">The current <see cref="SagaContext"/> assocaited with the pending saga modifications.</param>
 public virtual void PreSave(Saga saga, SagaContext context)
 {
 }
 /// <summary>
 ///  When overriden, defines the custom behavior to be invoked after attempting to save the specified <paramref name="saga"/>.
 /// </summary>
 /// <param name="saga">The modified <see cref="Saga"/> instance if <paramref name="error"/> is <value>null</value>; otherwise the original <see cref="Saga"/> instance if <paramref name="error"/> is not <value>null</value>.</param>
 /// <param name="context">The current <see cref="SagaContext"/> assocaited with the pending saga modifications.</param>
 /// <param name="error">The <see cref="Exception"/> thrown if the save was unsuccessful; otherwise <value>null</value>.</param>
 public virtual void PostSave(Saga saga, SagaContext context, Exception error)
 {
 }
        /// <summary>
        /// Save the specified <paramref name="context"/> changes for the given <paramref name="saga"/>.
        /// </summary>
        /// <param name="saga">The current saga version for which the context applies.</param>
        /// <param name="context">The saga context containing the saga changes to be applied.</param>
        public Saga Save(Saga saga, SagaContext context)
        {
            var result = sagaStore.Save(saga, context);

            if (saga.Version == 1)
            {
                statistics.IncrementInsertCount();
            }
            else
            {
                if (saga.Completed)
                {
                    statistics.IncrementDeleteCount();
                }
                else
                {
                    statistics.IncrementUpdateCount();
                }
            }

            return result;
        }