/// <summary> /// Retrieves a <see cref="IContainSagaData"/> instance. Used when implementing a <see cref="IFindSagas{T}"/>. /// </summary> /// <typeparam name="TSagaData">The <see cref="IContainSagaData"/> type to return.</typeparam> /// <param name="session">Used to provide an extension point and access the current <see cref="DbConnection"/> and <see cref="DbTransaction"/>.</param> /// <param name="context">Used to append a concurrency value that can be verified when the SagaData is persisted.</param> /// <param name="whereClause">The SQL where clause to append to the retrieve saga SQL statement.</param> /// <param name="appendParameters">Used to append <see cref="DbParameter"/>s used in the <paramref name="whereClause"/>.</param> public static Task <TSagaData> GetSagaData <TSagaData>(this SynchronizedStorageSession session, ReadOnlyContextBag context, string whereClause, ParameterAppender appendParameters) where TSagaData : class, IContainSagaData { Guard.AgainstNull(nameof(session), session); Guard.AgainstNull(nameof(context), context); Guard.AgainstNull(nameof(appendParameters), appendParameters); Guard.AgainstNullAndEmpty(nameof(whereClause), whereClause); var writableContextBag = (ContextBag)context; var sqlSession = session.GetSqlStorageSession(); return(SagaPersister.GetByWhereClause <TSagaData>(whereClause, session, writableContextBag, appendParameters, sqlSession.InfoCache)); }
/// <summary> /// Retrieves a <see cref="IContainSagaData"/> instance. Used when implementing a <see cref="IFindSagas{T}"/>. /// </summary> /// <typeparam name="TSagaData">The <see cref="IContainSagaData"/> type to return.</typeparam> /// <param name="session">Used to provide an extension point and access the current <see cref="DbConnection"/> and <see cref="DbTransaction"/>.</param> /// <param name="context">Used to append a concurrency value that can be verified when the SagaData is persisted.</param> /// <param name="whereClause">The SQL where clause to append to the retrieve saga SQL statement.</param> /// <param name="appendParameters">Used to append <see cref="DbParameter"/>s used in the <paramref name="whereClause"/>.</param> public static Task <TSagaData> GetSagaData <TSagaData>(this SynchronizedStorageSession session, ReadOnlyContextBag context, string whereClause, ParameterAppender appendParameters) where TSagaData : class, IContainSagaData { Guard.AgainstNull(nameof(session), session); Guard.AgainstNull(nameof(context), context); Guard.AgainstNull(nameof(appendParameters), appendParameters); Guard.AgainstNullAndEmpty(nameof(whereClause), whereClause); var writableContextBag = (ContextBag)context; var sqlSession = session.GetSqlStorageSession(); if (sqlSession.InfoCache == null) { throw new Exception("Cannot load saga data because the Sagas feature is disabled in the endpoint."); } return(SagaPersister.GetByWhereClause <TSagaData>(whereClause, session, writableContextBag, appendParameters, sqlSession.InfoCache)); }
/// <summary> /// Retrieves a <see cref="IContainSagaData"/> instance. Used when implementing a <see cref="IFindSagas{T}"/>. /// </summary> /// <typeparam name="TSagaData">The <see cref="IContainSagaData"/> type to return.</typeparam> /// <param name="session">Used to provide an extension point and access the current <see cref="DbConnection"/> and <see cref="DbTransaction"/>.</param> /// <param name="context">Used to append a concurrency value that can be verified when the SagaData is persisted.</param> /// <param name="whereClause">The SQL where clause to append to the retrieve saga SQL statement.</param> /// <param name="appendParameters">Used to append <see cref="DbParameter"/>s used in the <paramref name="whereClause"/>.</param> public static Task <TSagaData> GetSagaData <TSagaData>(this SynchronizedStorageSession session, ReadOnlyContextBag context, string whereClause, ParameterAppender appendParameters) where TSagaData : IContainSagaData { Guard.AgainstNull(nameof(session), session); Guard.AgainstNull(nameof(context), context); Guard.AgainstNull(nameof(appendParameters), appendParameters); Guard.AgainstNullAndEmpty(nameof(whereClause), whereClause); var writableContextBag = (ContextBag)context; var sqlSession = session.GetSqlStorageSession(); var sagaInfoCache = sqlSession.InfoCache; if (sagaInfoCache == null) { throw new Exception($"{nameof(GetSagaData)} can only be executed when Sagas have been enabled on the endpoint."); } return(SagaPersister.GetByWhereClause <TSagaData>(whereClause, session, writableContextBag, appendParameters, sagaInfoCache)); }
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); } } }
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); } } }