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)); }
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); }
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); }
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); } }
/// <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)); }
/// <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); }
/// <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(); })); }
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); }
/// <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)); } } }
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); } }
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); }
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); }
/// <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++; }
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))); }
private ISagaData GetSagaData(Saga saga) { var getter = _sagaDataGetters.GetOrAdd(saga.GetType(), x => x.GetProperty("Data")); return(getter.GetValue(saga, null) as ISagaData); }
/// <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)); }
/// <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++; }