static ActiveSagaInstance AssociateSagaWithMessage(FakeSaga saga, IInvokeHandlerContext behaviorContext)
        {
            var sagaInstance = new ActiveSagaInstance(saga, SagaMetadata.Create(typeof(FakeSaga), new List <Type>(), new Conventions()), () => DateTime.UtcNow);

            behaviorContext.Extensions.Set(sagaInstance);
            return(sagaInstance);
        }
    void AuditSaga(ActiveSagaInstance activeSagaInstance, IInvokeHandlerContext context)
    {
        var saga = activeSagaInstance.Instance;

        if (saga.Entity == null)
        {
            if (context.IsTimeoutMessage())
            {
                //Receiving a timeout for a saga that has completed
                return;
            }
            throw new Exception("Expected saga.Entity to contain a value.");
        }
        if (!context.Headers.TryGetValue(Headers.MessageId, out var messageId))
        {
            return;
        }

        var headers             = context.Headers;
        var originatingMachine  = headers["NServiceBus.OriginatingMachine"];
        var originatingEndpoint = headers[Headers.OriginatingEndpoint];
        var intent = context.MessageIntent();

        var initiator = new SagaChangeInitiator
        {
            IsSagaTimeoutMessage = context.IsTimeoutMessage(),
            InitiatingMessageId  = messageId,
            OriginatingMachine   = originatingMachine,
            OriginatingEndpoint  = originatingEndpoint,
            MessageType          = context.MessageMetadata.MessageType.FullName,
            TimeSent             = context.TimeSent(),
            Intent = intent
        };

        sagaAudit.IsNew       = activeSagaInstance.IsNew;
        sagaAudit.IsCompleted = saga.Completed;
        sagaAudit.SagaId      = saga.Entity.Id;

        AssignSagaStateChangeCausedByMessage(context);

        logger.WriteInfo(
            messageTemplate: messageTemplate,
            properties: new[]
        {
            new LogEventProperty("SagaType", new ScalarValue(sagaAudit.SagaType)),
            new LogEventProperty("SagaId", new ScalarValue(sagaAudit.SagaId)),
            new LogEventProperty("StartTime", new ScalarValue(sagaAudit.StartTime)),
            new LogEventProperty("FinishTime", new ScalarValue(sagaAudit.FinishTime)),
            new LogEventProperty("IsCompleted", new ScalarValue(sagaAudit.IsCompleted)),
            new LogEventProperty("IsNew", new ScalarValue(sagaAudit.IsNew)),
            new LogEventProperty("SagaType", new ScalarValue(sagaAudit.SagaType)),
            logger.BindProperty("Initiator", initiator),
            logger.BindProperty("ResultingMessages", sagaAudit.ResultingMessages),
            logger.BindProperty("Entity", saga.Entity),
        });
    }
        void LogSaga(ActiveSagaInstance saga, IncomingContext context)
        {
            var audit = string.Format("{0}:{1}", saga.Metadata.Name, saga.SagaId);

            string header;

            if (context.IncomingLogicalMessage.Headers.TryGetValue(Headers.InvokedSagas, out header))
            {
                context.IncomingLogicalMessage.Headers[Headers.InvokedSagas] += string.Format(";{0}", audit);
            }
            else
            {
                context.IncomingLogicalMessage.Headers[Headers.InvokedSagas] = audit;
            }
        }
Beispiel #4
0
        bool IsAllowedToStartANewSaga(IncomingContext context, ActiveSagaInstance sagaInstanceState)
        {
            string sagaType;

            if (context.IncomingLogicalMessage.Headers.ContainsKey(Headers.SagaId) &&
                context.IncomingLogicalMessage.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(sagaInstanceState.SagaType.FullName))
                {
                    //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(SagaConfigurationCache.IsAStartSagaMessage(sagaInstanceState.SagaType, context.IncomingLogicalMessage.MessageType));
        }
Beispiel #5
0
    void AuditSaga(ActiveSagaInstance activeSagaInstance, IInvokeHandlerContext context, SagaUpdatedMessage sagaAudit)
    {
        var saga = activeSagaInstance.Instance;

        if (saga.Entity == null)
        {
            //this can happen if it is a timeout or for invoking "saga not found" logic
            return;
        }

        var headers = context.Headers;

        if (!headers.TryGetValue(Headers.MessageId, out var messageId))
        {
            return;
        }

        var intent = context.MessageIntent();

        sagaAudit.IsNew       = activeSagaInstance.IsNew;
        sagaAudit.IsCompleted = saga.Completed;
        sagaAudit.SagaId      = saga.Entity.Id;

        AssignSagaStateChangeCausedByMessage(context, sagaAudit);

        List <LogEventProperty> properties = new()
        {
            new("SagaType", new ScalarValue(sagaAudit.SagaType)),
            new("SagaId", new ScalarValue(sagaAudit.SagaId)),
            new("StartTime", new ScalarValue(sagaAudit.StartTime)),
            new("FinishTime", new ScalarValue(sagaAudit.FinishTime)),
            new("IsCompleted", new ScalarValue(sagaAudit.IsCompleted)),
            new("IsNew", new ScalarValue(sagaAudit.IsNew))
        };

        var logger      = context.Logger();
        var messageType = TypeNameConverter.GetName(context.MessageType());

        Dictionary <ScalarValue, LogEventPropertyValue> initiator = new()
        {
            { new("IsSagaTimeout"), new ScalarValue(context.IsTimeoutMessage()) },
            { new("MessageId"), new ScalarValue(messageId) },
Beispiel #6
0
        Task AuditSaga(ActiveSagaInstance activeSagaInstance, SagaUpdatedMessage sagaAudit, IInvokeHandlerContext context)
        {
            if (!context.Headers.TryGetValue(Headers.MessageId, out var messageId))
            {
                return(Task.FromResult(0));
            }

            var saga = activeSagaInstance.Instance;

            string sagaStateString;

            if (customSagaEntitySerialization != null)
            {
                sagaStateString = SimpleJson.SerializeObject(customSagaEntitySerialization(saga.Entity));
            }
            else
            {
                sagaStateString = SimpleJson.SerializeObject(saga.Entity, sagaEntitySerializationStrategy);
            }

            var messageType = context.MessageMetadata.MessageType.FullName;
            var headers     = context.MessageHeaders;

            sagaAudit.StartTime   = activeSagaInstance.Created.UtcDateTime;
            sagaAudit.FinishTime  = activeSagaInstance.Modified.UtcDateTime;
            sagaAudit.Initiator   = BuildSagaChangeInitiatorMessage(headers, messageId, messageType);
            sagaAudit.IsNew       = activeSagaInstance.IsNew;
            sagaAudit.IsCompleted = saga.Completed;
            sagaAudit.Endpoint    = endpointName;
            sagaAudit.SagaId      = saga.Entity.Id;
            sagaAudit.SagaType    = saga.GetType().FullName;
            sagaAudit.SagaState   = sagaStateString;

            AssignSagaStateChangeCausedByMessage(context, activeSagaInstance, sagaAudit);

            var transportTransaction = context.Extensions.Get <TransportTransaction>();

            return(backend.Send(sagaAudit, transportTransaction, context.CancellationToken));
        }
Beispiel #7
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();
            }
        }
 static bool HasBeenFound(ActiveSagaInstance saga)
 {
     return(!saga.NotFound);
 }
Beispiel #9
0
        static void AssignSagaStateChangeCausedByMessage(IInvokeHandlerContext context, ActiveSagaInstance sagaInstance, SagaUpdatedMessage sagaAudit)
        {
            if (!context.MessageHeaders.TryGetValue(SagaAuditHeaders.SagaStateChange, out var sagaStateChange))
            {
                sagaStateChange = string.Empty;
            }

            var statechange = "Updated";

            if (sagaInstance.IsNew)
            {
                statechange = "New";
            }
            if (sagaInstance.Instance.Completed)
            {
                statechange = "Completed";
            }

            if (!string.IsNullOrEmpty(sagaStateChange))
            {
                sagaStateChange += ";";
            }
            sagaStateChange += $"{sagaAudit.SagaId}:{statechange}";

            context.Headers[SagaAuditHeaders.SagaStateChange] = sagaStateChange;
        }
        public void Invoke(IncomingContext context, Action next)
        {
            currentContext = context;

            // We need this for backwards compatibility because in v4.0.0 we still have this headers being sent as part of the message even if MessageIntent == MessageIntentEnum.Publish
            if (context.PhysicalMessage.MessageIntent == MessageIntentEnum.Publish)
            {
                context.PhysicalMessage.Headers.Remove(Headers.SagaId);
                context.PhysicalMessage.Headers.Remove(Headers.SagaType);
            }

            var saga = context.MessageHandler.Instance as Saga.Saga;

            if (saga == null)
            {
                next();
                return;
            }

            var sagaMetadata = SagaMetaModel.FindByName(context.MessageHandler.Instance.GetType().FullName);


            var sagaInstanceState = new ActiveSagaInstance(saga, sagaMetadata);

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

            var loadedEntity = TryLoadSagaEntity(sagaMetadata, context.IncomingLogicalMessage);

            if (loadedEntity == null)
            {
                //if this message are not allowed to start the saga
                if (IsMessageAllowedToStartTheSaga(context.IncomingLogicalMessage, sagaMetadata))
                {
                    sagaInstanceState.AttachNewEntity(CreateNewSagaEntity(sagaMetadata, context.IncomingLogicalMessage));
                }
                else
                {
                    sagaInstanceState.MarkAsNotFound();

                    //we don't invoke not found handlers for timeouts since
                    if (IsTimeoutMessage(context.IncomingLogicalMessage))
                    {
                        logger.InfoFormat("No saga found for timeout message {0}, ignoring since the saga has been marked as complete before the timeout fired", context.PhysicalMessage.Id);
                    }
                    else
                    {
                        InvokeSagaNotFoundHandlers(sagaMetadata);
                    }
                }
            }
            else
            {
                sagaInstanceState.AttachExistingEntity(loadedEntity);
            }

            if (IsTimeoutMessage(context.IncomingLogicalMessage))
            {
                context.MessageHandler.Invocation = MessageHandlerRegistry.InvokeTimeout;
            }


            next();

            if (sagaInstanceState.NotFound)
            {
                return;
            }
            sagaInstanceState.ValidateIdHasNotChanged();
            LogSaga(sagaInstanceState, context);

            if (saga.Completed)
            {
                if (!sagaInstanceState.IsNew)
                {
                    SagaPersister.Complete(saga.Entity);
                }

                if (saga.Entity.Id != Guid.Empty)
                {
                    NotifyTimeoutManagerThatSagaHasCompleted(saga);
                }

                logger.DebugFormat("Saga: '{0}' with Id: '{1}' has completed.", sagaInstanceState.Metadata.Name, saga.Entity.Id);
            }
            else
            {
                if (sagaInstanceState.IsNew)
                {
                    SagaPersister.Save(saga.Entity);
                }
                else
                {
                    SagaPersister.Update(saga.Entity);
                }
            }
        }
Beispiel #11
0
    void AuditSaga(ActiveSagaInstance activeSagaInstance, IInvokeHandlerContext context)
    {
        var saga = activeSagaInstance.Instance;

        if (saga.Entity == null)
        {
            if (context.IsTimeoutMessage())
            {
                //Receiving a timeout for a saga that has completed
                return;
            }

            throw new Exception("Expected saga.Entity to contain a value.");
        }

        var headers = context.Headers;

        if (!headers.TryGetValue(Headers.MessageId, out var messageId))
        {
            return;
        }

        var intent = context.MessageIntent();

        var initiator = new SagaChangeInitiator
        {
            IsSagaTimeoutMessage = context.IsTimeoutMessage(),
            InitiatingMessageId  = messageId,
            OriginatingMachine   = context.OriginatingMachine(),
            OriginatingEndpoint  = context.OriginatingEndpoint(),
            MessageType          = context.MessageName(),
            TimeSent             = context.TimeSent(),
            Intent = intent
        };

        sagaAudit.IsNew       = activeSagaInstance.IsNew;
        sagaAudit.IsCompleted = saga.Completed;
        sagaAudit.SagaId      = saga.Entity.Id;

        AssignSagaStateChangeCausedByMessage(context);

        var properties = new List <LogEventProperty>
        {
            new LogEventProperty("SagaType", new ScalarValue(sagaAudit.SagaType)),
            new LogEventProperty("SagaId", new ScalarValue(sagaAudit.SagaId)),
            new LogEventProperty("StartTime", new ScalarValue(sagaAudit.StartTime)),
            new LogEventProperty("FinishTime", new ScalarValue(sagaAudit.FinishTime)),
            new LogEventProperty("IsCompleted", new ScalarValue(sagaAudit.IsCompleted)),
            new LogEventProperty("IsNew", new ScalarValue(sagaAudit.IsNew)),
            new LogEventProperty("SagaType", new ScalarValue(sagaAudit.SagaType)),
        };

        var logger = context.Logger();

        if (logger.BindProperty("Initiator", initiator, out var initiatorProperty))
        {
            properties.Add(initiatorProperty);
        }

        if (logger.BindProperty("ResultingMessages", sagaAudit.ResultingMessages, out var resultingMessagesProperty))
        {
            properties.Add(resultingMessagesProperty);
        }

        if (logger.BindProperty("Entity", saga.Entity, out var sagaEntityProperty))
        {
            properties.Add(sagaEntityProperty);
        }

        logger.WriteInfo(messageTemplate, properties);
    }
Beispiel #12
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();
            }
        }
    void AuditSaga(ActiveSagaInstance activeSagaInstance, IInvokeHandlerContext context, SagaUpdatedMessage sagaAudit)
    {
        var saga = activeSagaInstance.Instance;

        if (saga.Entity == null)
        {
            //this can happen if it is a timeout or for invoking "saga not found" logic
            return;
        }

        var headers = context.Headers;

        if (!headers.TryGetValue(Headers.MessageId, out var messageId))
        {
            return;
        }

        var intent = context.MessageIntent();

        var initiator = new SagaChangeInitiator
                        (
            isSagaTimeoutMessage: context.IsTimeoutMessage(),
            initiatingMessageId: messageId,
            originatingMachine: context.OriginatingMachine(),
            originatingEndpoint: context.OriginatingEndpoint(),
            messageType: context.MessageType(),
            timeSent: context.TimeSent(),
            intent: intent
                        );

        sagaAudit.IsNew       = activeSagaInstance.IsNew;
        sagaAudit.IsCompleted = saga.Completed;
        sagaAudit.SagaId      = saga.Entity.Id;

        AssignSagaStateChangeCausedByMessage(context, sagaAudit);

        var properties = new List <LogEventProperty>
        {
            new LogEventProperty("SagaType", new ScalarValue(sagaAudit.SagaType)),
            new LogEventProperty("SagaId", new ScalarValue(sagaAudit.SagaId)),
            new LogEventProperty("StartTime", new ScalarValue(sagaAudit.StartTime)),
            new LogEventProperty("FinishTime", new ScalarValue(sagaAudit.FinishTime)),
            new LogEventProperty("IsCompleted", new ScalarValue(sagaAudit.IsCompleted)),
            new LogEventProperty("IsNew", new ScalarValue(sagaAudit.IsNew)),
            new LogEventProperty("SagaType", new ScalarValue(sagaAudit.SagaType)),
        };

        var logger = context.Logger();

        if (logger.BindProperty("Initiator", initiator, out var initiatorProperty))
        {
            properties.Add(initiatorProperty);
        }

        if (logger.BindProperty("ResultingMessages", sagaAudit.ResultingMessages, out var resultingMessagesProperty))
        {
            properties.Add(resultingMessagesProperty);
        }

        if (logger.BindProperty("Entity", saga.Entity, out var sagaEntityProperty))
        {
            properties.Add(sagaEntityProperty);
        }

        logger.WriteInfo(messageTemplate, properties);
    }
Beispiel #14
0
        public void Invoke(IncomingContext context, Action next)
        {
            // We need this for backwards compatibility because in v4.0.0 we still have this headers being sent as part of the message even if MessageIntent == MessageIntentEnum.Publish
            if (context.PhysicalMessage.MessageIntent == MessageIntentEnum.Publish)
            {
                context.PhysicalMessage.Headers.Remove(Headers.SagaId);
                context.PhysicalMessage.Headers.Remove(Headers.SagaType);
            }

            var saga = context.MessageHandler.Instance as Saga.Saga;

            if (saga == null)
            {
                next();
                return;
            }

            currentContext = context;

            var sagaInstanceState = new ActiveSagaInstance(saga);

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

            var loadedEntity = TryLoadSagaEntity(saga, context.IncomingLogicalMessage);

            if (loadedEntity == null)
            {
                //if this message are not allowed to start the saga
                if (IsAllowedToStartANewSaga(context, sagaInstanceState))
                {
                    sagaInstanceState.AttachNewEntity(CreateNewSagaEntity(sagaInstanceState.SagaType));
                }
                else
                {
                    sagaInstanceState.MarkAsNotFound();

                    InvokeSagaNotFoundHandlers(sagaInstanceState.SagaType);
                }
            }
            else
            {
                sagaInstanceState.AttachExistingEntity(loadedEntity);
            }

            if (IsTimeoutMessage(context.IncomingLogicalMessage))
            {
                context.MessageHandler.Invocation = MessageHandlerRegistry.InvokeTimeout;
            }


            next();

            if (sagaInstanceState.NotFound)
            {
                return;
            }

            LogSaga(sagaInstanceState, context);

            if (saga.Completed)
            {
                if (!sagaInstanceState.IsNew)
                {
                    SagaPersister.Complete(saga.Entity);
                }

                if (saga.Entity.Id != Guid.Empty)
                {
                    NotifyTimeoutManagerThatSagaHasCompleted(saga);
                }

                logger.Debug(string.Format("Saga: '{0}' with Id: '{1}' has completed.", sagaInstanceState.SagaType.FullName, saga.Entity.Id));
            }
            else
            {
                if (sagaInstanceState.IsNew)
                {
                    SagaPersister.Save(saga.Entity);
                }
                else
                {
                    SagaPersister.Update(saga.Entity);
                }
            }
        }