Ejemplo n.º 1
0
        private bool IsLockedSaga(Saga saga)
        {
            var property = _sagaDataCache.GetOrAdd(saga.GetType(), x => saga.GetType().GetProperty(nameof(Saga <ISagaData> .Data)));
            var sagaData = property.GetValue(saga) as ISagaData;

            return(!saga.IsNew && MessageDeferringSagaPersister.IsLockedSagaData(sagaData));
        }
Ejemplo n.º 2
0
        private ISagaWithCreatedOn GetSagaData(Saga saga)
        {
            var property = _sagaDataCache.GetOrAdd(saga.GetType(), x => saga.GetType().GetProperty(nameof(Saga <ISagaData> .Data)));
            var data     = property.GetValue(saga) as ISagaWithCreatedOn;

            Guard.Against <InvalidOperationException>(data == null,
                                                      $"SagaData for saga {saga.GetType()} does not implemente {nameof(ISagaWithCreatedOn)}?!"
                                                      );

            return(data);
        }
Ejemplo n.º 3
0
        private ISagaManager CreateManagerForSaga(Saga saga)
        {
            var sagaType     = saga.GetType();
            var sagaDataType = saga.GetType().FindBaseType(
                p => p.IsGenericType && p.GetGenericTypeDefinition() == typeof(Saga <>)).GetGenericArguments()[0];

            if (sagaDataType == null)
            {
                throw new InvalidOperationException("Saga not derived from Saga<T>");
            }

            var manager = typeof(SagaManager <,>).MakeGenericType(sagaType, sagaDataType);

            return((ISagaManager)Activator.CreateInstance(manager, _finder));
        }
        void AuditSaga(Saga saga, IncomingContext context)
        {
            string messageId;

            if (!context.IncomingLogicalMessage.Headers.TryGetValue(Headers.MessageId, out messageId))
            {
                return;
            }

            var activeSagaInstance = context.Get <ActiveSagaInstance>();
            var sagaStateString    = Serializer.Serialize(saga.Entity);
            var messageType        = context.IncomingLogicalMessage.MessageType.FullName;
            var headers            = context.IncomingLogicalMessage.Headers;

            sagaAudit.Initiator   = BuildSagaChangeInitatorMessage(headers, messageId, messageType);
            sagaAudit.IsNew       = activeSagaInstance.IsNew;
            sagaAudit.IsCompleted = saga.Completed;
            sagaAudit.Endpoint    = configure.Settings.EndpointName();
            sagaAudit.SagaId      = saga.Entity.Id;
            sagaAudit.SagaType    = saga.GetType().FullName;
            sagaAudit.SagaState   = sagaStateString;

            AssignSagaStateChangeCausedByMessage(context);
            backend.Send(sagaAudit);
        }
Ejemplo n.º 5
0
        void InnerHandle <TMessage>(TMessage message)
        {
            var existingSagaData = availableSagaData.SingleOrDefault(data => Correlates(correlations, message, data));

            if (existingSagaData != null)
            {
                saga.Data  = existingSagaData;
                saga.IsNew = false;
                if (CorrelatedWithExistingSagaData != null)
                {
                    CorrelatedWithExistingSagaData(message, saga.Data);
                }
                Dispatch(message);
                if (saga.Complete)
                {
                    availableSagaData.Remove(saga.Data);
                    deletedSagaData.Add(saga.Data);
                }
                return;
            }

            if (saga.GetType().GetInterfaces().Contains(typeof(IAmInitiatedBy <TMessage>)))
            {
                saga.Data  = new T();
                saga.IsNew = true;
                if (CreatedNewSagaData != null)
                {
                    CreatedNewSagaData(message, saga.Data);
                }
                Dispatch(message);
                if (!saga.Complete)
                {
                    availableSagaData.Add(saga.Data);
                }
                else
                {
                    deletedSagaData.Add(saga.Data);
                }
                return;
            }

            if (CouldNotCorrelate != null)
            {
                CouldNotCorrelate(message);
            }
        }
Ejemplo n.º 6
0
    /// <summary>
    /// Gets (most likely from a cache) the set of correlation properties relevant for the given saga handler.
    /// </summary>
    public SagaDataCorrelationProperties GetCorrelationProperties(object message, Saga saga)
    {
        var sagaType     = saga.GetType();
        var sagaDataType = saga.GetSagaDataType();
        var key          = $"{sagaType.FullName}/{sagaDataType.FullName}";

        var correlationPropertiesForThisSagaDataType = _cachedCorrelationProperties
                                                       .GetOrAdd(key, _ => GetCorrelationProperties(saga));

        return(new SagaDataCorrelationProperties(correlationPropertiesForThisSagaDataType, sagaDataType));
    }
Ejemplo n.º 7
0
        /// <summary>
        /// Gets (most likely from a cache) the set of correlation properties relevant for the given saga handler.
        /// </summary>
        public SagaDataCorrelationProperties GetCorrelationProperties(object message, Saga saga)
        {
            var sagaType = saga.GetType();
            var sagaDataType = saga.GetSagaDataType();
            var key = $"{sagaType.FullName}/{sagaDataType.FullName}";

            var correlationPropertiesForThisSagaDataType = _cachedCorrelationProperties
                .GetOrAdd(key, _ => GetCorrelationProperties(saga));

            return new SagaDataCorrelationProperties(correlationPropertiesForThisSagaDataType, sagaDataType);
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Gets whether a message of the given type is allowed to cause a new saga data instance to be created
        /// </summary>
        public bool CanBeInitiatedBy(Type messageType)
        {
            // checks if IAmInitiatedBy<TMessage> is implemented by the saga
            return(CanBeInitiatedByCache
                   .GetOrAdd($"{Handler.GetType().FullName}::{messageType.FullName}", _ =>
            {
                var implementedInterfaces = Saga.GetType().GetInterfaces();

                var handlerTypesToLookFor = new[] { messageType }.Concat(messageType.GetBaseTypes())
                .Select(baseType => typeof(IAmInitiatedBy <>).MakeGenericType(baseType));

                return implementedInterfaces.Intersect(handlerTypesToLookFor).Any();
            }));
        }
Ejemplo n.º 9
0
        private string[] GetSagaDataPropertyPathsToIndex(Saga saga)
        {
            string[] paths;
            var      sagaType = saga.GetType();

            if (fieldsToIndexForGivenSagaDataType.TryGetValue(sagaType, out paths))
            {
                // yay! GO!
                return(paths);
            }

            paths = saga.Correlations.Values.Select(v => v.SagaDataPropertyPath).ToArray();

            // make sure they're there the next time
            fieldsToIndexForGivenSagaDataType[sagaType] = paths;

            return(paths);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Delete an existing saga instance.
        /// </summary>
        /// <param name="saga">The saga instance to delete.</param>
        private void DeleteSaga(Saga saga)
        {
            var typeId = GetSagaTypeId(saga.GetType());

            using (var command = dialect.CreateCommand(dialect.DeleteSaga))
            {
                Log.Trace("Completing existing saga {0}", saga);

                command.Parameters.Add(dialect.CreateTypeIdParameter(typeId));
                command.Parameters.Add(dialect.CreateIdParameter(saga.CorrelationId));
                command.Parameters.Add(dialect.CreateVersionParameter(saga.Version));

                if (dialect.ExecuteNonQuery(command) == 0)
                {
                    throw new ConcurrencyException(Exceptions.SagaConcurrencyConflict.FormatWith(saga.GetType(), saga.CorrelationId));
                }
            }
        }
Ejemplo n.º 11
0
        public static void AddOrUpdateInvokedSagasHeader(Dictionary <string, string> headers, Saga sagaInstance)
        {
            if (sagaInstance.Entity == null)
            {
                return;
            }

            var invokedSagaAuditData = $"{sagaInstance.GetType().FullName}:{sagaInstance.Entity.Id}";

            if (headers.TryGetValue(SagaAuditHeaders.InvokedSagas, out var invokedSagasHeader))
            {
                headers[SagaAuditHeaders.InvokedSagas] = $"{invokedSagasHeader};{invokedSagaAuditData}";
            }
            else
            {
                headers.Add(SagaAuditHeaders.InvokedSagas, invokedSagaAuditData);
            }
        }
Ejemplo n.º 12
0
        private ISagaData GetSagaData <TMessage>(TMessage message, Saga saga)
        {
            var correlations = saga.Correlations;

            if (!correlations.ContainsKey(typeof(TMessage)))
            {
                return(null);
            }

            var correlation          = correlations[typeof(TMessage)];
            var fieldFromMessage     = correlation.FieldFromMessage(message);
            var sagaDataPropertyPath = correlation.SagaDataPropertyPath;
            var sagaDataType         = saga.GetType().GetProperty("Data").PropertyType;

            var sagaData = storeSagaData.GetType()
                           .GetMethod("Find").MakeGenericMethod(sagaDataType)
                           .Invoke(storeSagaData, new[] { sagaDataPropertyPath, fieldFromMessage ?? "" });

            return((ISagaData)sagaData);
        }
Ejemplo n.º 13
0
        ISagaData GetSagaData <TMessage>(TMessage message, Saga saga)
        {
            var sagaDataType = saga.GetType().GetProperty(sagaDataPropertyName).PropertyType;

            var correlations = saga.Correlations;

            // if correlation is set up, insist on using it
            if (correlations.ContainsKey(typeof(TMessage)))
            {
                var correlation          = correlations[typeof(TMessage)];
                var fieldFromMessage     = correlation.FieldFromMessage(message);
                var sagaDataPropertyPath = correlation.SagaDataPropertyPath;

                var sagaData = GetSagaData(sagaDataType, sagaDataPropertyPath, fieldFromMessage);

                return((ISagaData)sagaData);
            }

            // otherwise, see if we can do auto-correlation
            if (MessageContext.HasCurrent)
            {
                var messageContext = MessageContext.GetCurrent();

                // if the incoming message contains a saga auto-correlation id, try to load that specific saga
                if (messageContext.Headers.ContainsKey(Headers.AutoCorrelationSagaId))
                {
                    var sagaId = messageContext.Headers[Headers.AutoCorrelationSagaId].ToString();
                    var data   = GetSagaData(sagaDataType, sagaDataIdPropertyName, sagaId);

                    // if we found the saga, return it
                    if (data != null)
                    {
                        return((ISagaData)data);
                    }
                }
            }

            // last option: bail out :)
            return(null);
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Insert a new saga instance.
        /// </summary>
        /// <param name="saga">The saga instance to insert.</param>
        private void InsertSaga(Saga saga)
        {
            var state  = serializer.Serialize(saga);
            var typeId = GetSagaTypeId(saga.GetType());

            using (var command = dialect.CreateCommand(dialect.InsertSaga))
            {
                Log.Trace("Starting new saga {0}", saga);

                command.Parameters.Add(dialect.CreateTypeIdParameter(typeId));
                command.Parameters.Add(dialect.CreateIdParameter(saga.CorrelationId));
                command.Parameters.Add(dialect.CreateTimeoutParameter(saga.Timeout));
                command.Parameters.Add(dialect.CreateStateParameter(state));

                if (dialect.ExecuteNonQuery(command) == 0)
                {
                    throw new ConcurrencyException(Exceptions.SagaConcurrencyConflict.FormatWith(saga.GetType(), saga.CorrelationId));
                }
            }

            saga.Version++;
        }
Ejemplo n.º 15
0
 internal ActiveSagaInstance(Saga saga)
 {
     Instance = saga;
     SagaType = saga.GetType();
 }
 protected EventContext CreateEventContext(Saga saga, DateTime timeout)
 {
     return(new EventContext(saga.CorrelationId, HeaderCollection.Empty, new Timeout(saga.GetType(), timeout)));
 }
Ejemplo n.º 17
0
        private ISagaData GetSagaData(Saga saga)
        {
            var getter = _sagaDataGetters.GetOrAdd(saga.GetType(), x => x.GetProperty("Data"));

            return(getter.GetValue(saga, null) as ISagaData);
        }
Ejemplo n.º 18
0
 internal ActiveSagaInstance(Saga saga)
 {
     Instance = saga;
     SagaType = saga.GetType();
 }
Ejemplo n.º 19
0
        /// <summary>
        /// Delete an existing saga instance.
        /// </summary>
        /// <param name="saga">The saga instance to delete.</param>
        private void DeleteSaga(Saga saga)
        {
            var typeId = GetSagaTypeId(saga.GetType());

            using (var command = dialect.CreateCommand(dialect.DeleteSaga))
            {
                Log.Trace("Completing existing saga {0}", saga);

                command.Parameters.Add(dialect.CreateTypeIdParameter(typeId));
                command.Parameters.Add(dialect.CreateIdParameter(saga.CorrelationId));
                command.Parameters.Add(dialect.CreateVersionParameter(saga.Version));

                if (dialect.ExecuteNonQuery(command) == 0)
                    throw new ConcurrencyException(Exceptions.SagaConcurrencyConflict.FormatWith(saga.GetType(), saga.CorrelationId));
            }
        }
 protected EventContext CreateEventContext(Saga saga, DateTime timeout)
 {
     return new EventContext(saga.CorrelationId, HeaderCollection.Empty, new Timeout(saga.GetType(), timeout));
 }
Ejemplo n.º 21
0
        /// <summary>
        /// Update an existing saga instance.
        /// </summary>
        /// <param name="saga">The saga instance to update.</param>
        private void UpdateSaga(Saga saga)
        {
            var state = serializer.Serialize(saga);
            var typeId = GetSagaTypeId(saga.GetType());

            using (var command = dialect.CreateCommand(dialect.UpdateSaga))
            {
                Log.Trace("Updating existing saga {0}", saga);

                command.Parameters.Add(dialect.CreateTypeIdParameter(typeId));
                command.Parameters.Add(dialect.CreateIdParameter(saga.CorrelationId));
                command.Parameters.Add(dialect.CreateVersionParameter(saga.Version));
                command.Parameters.Add(dialect.CreateTimeoutParameter(saga.Timeout));
                command.Parameters.Add(dialect.CreateStateParameter(state));

                if (dialect.ExecuteNonQuery(command) == 0)
                    throw new ConcurrencyException(Exceptions.SagaConcurrencyConflict.FormatWith(saga.GetType(), saga.CorrelationId));
            }

            saga.Version++;
        }