Beispiel #1
0
        public async Task Invoke(IInvokeHandlerContext context, Func <IInvokeHandlerContext, Task> next)
        {
            var isTimeoutMessage = IsTimeoutMessage(context.Headers);
            var isTimeoutHandler = context.MessageHandler.IsTimeoutHandler;

            if (isTimeoutHandler && !isTimeoutMessage)
            {
                return;
            }

            if (!isTimeoutHandler && isTimeoutMessage)
            {
                return;
            }

            RemoveSagaHeadersIfProcessingAEvent(context);

            if (!(context.MessageHandler.Instance is Saga saga))
            {
                await next(context).ConfigureAwait(false);

                return;
            }

            var currentSagaMetadata = sagaMetadataCollection.Find(context.MessageHandler.Instance.GetType());

            if (context.Headers.TryGetValue(Headers.SagaType, out var targetSagaTypeString) && context.Headers.TryGetValue(Headers.SagaId, out var targetSagaId))
            {
                var targetSagaType = Type.GetType(targetSagaTypeString, false);

                if (targetSagaType == null)
                {
                    logger.Warn($"Saga headers indicated that the message was intended for {targetSagaTypeString} but that type isn't available. Will fallback to query persister for a saga instance of type {currentSagaMetadata.SagaType.FullName} and saga id {targetSagaId} instead");
                }
                else
                {
                    if (!sagaMetadataCollection.TryFind(targetSagaType, out var targetSagaMetaData))
                    {
                        logger.Warn($"Saga headers indicated that the message was intended for {targetSagaType.FullName} but no metadata was found for that saga type. Will fallback to query persister for a saga instance of type {currentSagaMetadata.SagaType.FullName} and saga id {targetSagaId} instead");
                    }
                    else
                    {
                        if (targetSagaMetaData.SagaType != currentSagaMetadata.SagaType)
                        {
                            //Message was intended for a different saga so no need to continue with this invocation
                            return;
                        }
                    }
                }
            }

            var sagaInstanceState = new ActiveSagaInstance(saga, currentSagaMetadata, () => DateTimeOffset.UtcNow);

            //so that other behaviors can access the saga
            context.Extensions.Set(sagaInstanceState);

            var loadedEntity = await TryLoadSagaEntity(currentSagaMetadata, context).ConfigureAwait(false);

            if (loadedEntity == null)
            {
                if (IsMessageAllowedToStartTheSaga(context, currentSagaMetadata))
                {
                    context.Extensions.Get <SagaInvocationResult>().SagaFound();
                    sagaInstanceState.AttachNewEntity(CreateNewSagaEntity(currentSagaMetadata, context));
                }
                else
                {
                    if (!context.Headers.ContainsKey(Headers.SagaId))
                    {
                        var finderDefinition = GetSagaFinder(currentSagaMetadata, context);
                        if (finderDefinition == null)
                        {
                            throw new Exception($"Message type {context.MessageBeingHandled.GetType().Name} is handled by saga {currentSagaMetadata.SagaType.Name}, but the saga does not contain a property mapping or custom saga finder to map the message to saga data. Consider adding a mapping in the saga's {nameof(Saga.ConfigureHowToFindSaga)} method.");
                        }
                    }

                    sagaInstanceState.MarkAsNotFound();

                    //we don't invoke not found handlers for timeouts
                    if (isTimeoutMessage)
                    {
                        context.Extensions.Get <SagaInvocationResult>().SagaFound();
                        logger.InfoFormat("No saga found for timeout message {0}, ignoring since the saga has been marked as complete before the timeout fired", context.MessageId);
                    }
                    else
                    {
                        context.Extensions.Get <SagaInvocationResult>().SagaNotFound();
                    }
                }
            }
            else
            {
                context.Extensions.Get <SagaInvocationResult>().SagaFound();
                sagaInstanceState.AttachExistingEntity(loadedEntity);
            }

            await next(context).ConfigureAwait(false);

            if (sagaInstanceState.NotFound)
            {
                return;
            }

            if (saga.Completed)
            {
                if (!sagaInstanceState.IsNew)
                {
                    await sagaPersister.Complete(saga.Entity, context.SynchronizedStorageSession, context.Extensions, context.CancellationToken).ConfigureAwait(false);
                }

                logger.DebugFormat("Saga: '{0}' with Id: '{1}' has completed.", sagaInstanceState.Metadata.Name, saga.Entity.Id);

                sagaInstanceState.Completed();
            }
            else
            {
                sagaInstanceState.ValidateChanges();

                if (sagaInstanceState.IsNew)
                {
                    var sagaCorrelationProperty = SagaCorrelationProperty.None;

                    if (sagaInstanceState.TryGetCorrelationProperty(out var correlationProperty))
                    {
                        sagaCorrelationProperty = new SagaCorrelationProperty(correlationProperty.PropertyInfo.Name, correlationProperty.PropertyInfo.GetValue(sagaInstanceState.Instance.Entity));
                    }

                    await sagaPersister.Save(saga.Entity, sagaCorrelationProperty, context.SynchronizedStorageSession, context.Extensions, context.CancellationToken).ConfigureAwait(false);
                }
                else
                {
                    await sagaPersister.Update(saga.Entity, context.SynchronizedStorageSession, context.Extensions, context.CancellationToken).ConfigureAwait(false);
                }

                sagaInstanceState.Updated();
            }
        }
Beispiel #2
0
        public async Task Invoke(IInvokeHandlerContext context, Func <IInvokeHandlerContext, Task> next)
        {
            var isTimeoutMessage = IsTimeoutMessage(context.Headers);
            var isTimeoutHandler = context.MessageHandler.IsTimeoutHandler;

            if (isTimeoutHandler && !isTimeoutMessage)
            {
                return;
            }

            if (!isTimeoutHandler && isTimeoutMessage)
            {
                return;
            }

            currentContext = context;

            RemoveSagaHeadersIfProcessingAEvent(context);

            var saga = context.MessageHandler.Instance as Saga;

            if (saga == null)
            {
                await next(context).ConfigureAwait(false);

                return;
            }

            var sagaMetadata      = sagaMetadataCollection.Find(context.MessageHandler.Instance.GetType());
            var sagaInstanceState = new ActiveSagaInstance(saga, sagaMetadata, () => DateTime.UtcNow);

            //so that other behaviors can access the saga
            context.Extensions.Set(sagaInstanceState);

            var loadedEntity = await TryLoadSagaEntity(sagaMetadata, context).ConfigureAwait(false);

            if (loadedEntity == null)
            {
                //if this message are not allowed to start the saga
                if (IsMessageAllowedToStartTheSaga(context, sagaMetadata))
                {
                    context.Extensions.Get <SagaInvocationResult>().SagaFound();
                    sagaInstanceState.AttachNewEntity(CreateNewSagaEntity(sagaMetadata, context));
                }
                else
                {
                    if (!context.Headers.ContainsKey(Headers.SagaId))
                    {
                        var finderDefinition = GetSagaFinder(sagaMetadata, context);
                        if (finderDefinition == null)
                        {
                            throw new Exception($"Message type {context.MessageBeingHandled.GetType().Name} is handled by saga {sagaMetadata.SagaType.Name}, but the saga does not contain a property mapping or custom saga finder to map the message to saga data. Consider adding a mapping in the saga's {nameof(Saga.ConfigureHowToFindSaga)} method.");
                        }
                    }

                    sagaInstanceState.MarkAsNotFound();

                    //we don't invoke not found handlers for timeouts
                    if (isTimeoutMessage)
                    {
                        context.Extensions.Get <SagaInvocationResult>().SagaFound();
                        logger.InfoFormat("No saga found for timeout message {0}, ignoring since the saga has been marked as complete before the timeout fired", context.MessageId);
                    }
                    else
                    {
                        context.Extensions.Get <SagaInvocationResult>().SagaNotFound();
                    }
                }
            }
            else
            {
                context.Extensions.Get <SagaInvocationResult>().SagaFound();
                sagaInstanceState.AttachExistingEntity(loadedEntity);
            }

            await next(context).ConfigureAwait(false);

            if (sagaInstanceState.NotFound)
            {
                return;
            }

            if (saga.Completed)
            {
                if (!sagaInstanceState.IsNew)
                {
                    await sagaPersister.Complete(saga.Entity, context.SynchronizedStorageSession, context.Extensions).ConfigureAwait(false);
                }

                if (saga.Entity.Id != Guid.Empty)
                {
                    await timeoutCancellation.CancelDeferredMessages(saga.Entity.Id.ToString(), context).ConfigureAwait(false);
                }

                logger.DebugFormat("Saga: '{0}' with Id: '{1}' has completed.", sagaInstanceState.Metadata.Name, saga.Entity.Id);

                sagaInstanceState.Completed();
            }
            else
            {
                sagaInstanceState.ValidateChanges();

                if (sagaInstanceState.IsNew)
                {
                    ActiveSagaInstance.CorrelationPropertyInfo correlationProperty;
                    var sagaCorrelationProperty = SagaCorrelationProperty.None;

                    if (sagaInstanceState.TryGetCorrelationProperty(out correlationProperty))
                    {
                        sagaCorrelationProperty = new SagaCorrelationProperty(correlationProperty.PropertyInfo.Name, correlationProperty.Value);
                    }

                    await sagaPersister.Save(saga.Entity, sagaCorrelationProperty, context.SynchronizedStorageSession, context.Extensions).ConfigureAwait(false);
                }
                else
                {
                    await sagaPersister.Update(saga.Entity, context.SynchronizedStorageSession, context.Extensions).ConfigureAwait(false);
                }

                sagaInstanceState.Updated();
            }
        }