static void EnsureMappingToStartMessage(SagaCorrelator correlator, Type sagaType, IEnumerable <SagaMessage> associatedMessages) { Contract.Requires(correlator != null); Contract.Requires(sagaType != null); Contract.Requires(associatedMessages != null); foreach (var message in associatedMessages.Where(message => message.StartsSaga)) { var messageType = message.MessageType.GetTypeInfo(); var matches = from mapping in correlator.Mappings where mapping.MessageType.GetTypeInfo().IsAssignableFrom(messageType) select mapping; if (!matches.Any()) { throw new SagaConfigurationException(SR.UnmappedSagaStartMessage.FormatDefault(message.MessageType.Name, sagaType.Name)); } } }
static SagaToMessageMap EnsureSinglePropertyMapping(SagaCorrelator correlator, IEnumerable <SagaMessage> associatedMessages, Type sagaType) { Contract.Requires(correlator != null); Contract.Requires(associatedMessages != null); Contract.Requires(sagaType != null); Contract.Ensures(Contract.Result <SagaToMessageMap>() != null); var propertyMappings = (from message in associatedMessages where message.StartsSaga from mapping in correlator.Mappings where mapping.MessageType == message.MessageType group mapping by mapping.SagaDataProperty.Name).ToArray(); switch (propertyMappings.Length) { case 0: throw new SagaConfigurationException(SR.SagaNotCorrelated); case 1: return(propertyMappings[0].First()); default: var messageProperties = new StringBuilder(); var names = from mappings in propertyMappings from mapping in mappings select mapping.MessageProperty.Name; using (var name = names.Distinct().Reverse().GetEnumerator()) { name.MoveNext(); messageProperties.Append(SR.AndConjunction); messageProperties.Append(name.Current); while (name.MoveNext()) { messageProperties.Insert(0, ", "); messageProperties.Insert(0, name.Current); } } throw new SagaConfigurationException(SR.TooManySagaCorrelationProperties.FormatDefault(sagaType.Name, messageProperties)); } }
static void SetSearchMethodForMappings( SagaCorrelator correlator, Type sagaType, Type sagaDataType, IEnumerable <SagaMessage> associatedMessages, ICollection <SagaSearchMethod> searchMethods) { Contract.Requires(correlator != null); Contract.Requires(sagaType != null); Contract.Requires(sagaDataType != null); Contract.Requires(associatedMessages != null); Contract.Requires(searchMethods != null); foreach (var mapping in correlator.Mappings) { var messageType = mapping.MessageType.GetTypeInfo(); var associatedMessage = associatedMessages.FirstOrDefault(message => messageType.IsAssignableFrom(message.MessageType.GetTypeInfo())); if (associatedMessage == null) { var interfaces = new[] { typeof(IStartWith <>), typeof(IStartWhen <>), typeof(IReceiveEvent <>), typeof(IHandleCommand <>) }; var interfaceNames = new StringBuilder(); using (var @interface = interfaces.Reverse().GetEnumerator()) { @interface.MoveNext(); interfaceNames.Append(SR.OrConjunction); interfaceNames.Append(@interface.Current.Name); while (@interface.MoveNext()) { interfaceNames.Insert(0, ", "); interfaceNames.Insert(0, @interface.Current.Name); } } throw new SagaConfigurationException(SR.UnhandledSagaMessage.FormatDefault(sagaType.Name, messageType.Name, interfaceNames)); } SetSearchMethod(mapping, sagaDataType, searchMethods); } }
/// <summary> /// Creates the metadata for a saga from the specified type. /// </summary> /// <param name="sagaType">The type of saga to create the metadata for.</param> /// <returns>A new <see cref="SagaMetadata"/> instance.</returns> /// <exception cref="ArgumentException"><paramref name="sagaType"/> is not a <see cref="ISaga{TData}"/>.</exception> public static SagaMetadata Create(Type sagaType) { if (!sagaType.IsSaga()) { throw new ArgumentException(SR.InvalidSaga.FormatDefault(sagaType.Name)); } var sagaDataType = sagaType.GetSagaDataType(); var method = CorrelateOfT.MakeGenericMethod(sagaDataType); var correlate = (Action <Type, ICorrelateSagaToMessage>)method.CreateDelegate(typeof(Action <Type, ICorrelateSagaToMessage>)); var correlator = new SagaCorrelator(); correlate(sagaType, correlator); var associatedMessages = GetAssociatedMessages(sagaType).ToArray(); var mapping = EnsureSinglePropertyMapping(correlator, associatedMessages, sagaType); var correlationProperty = mapping.SagaDataProperty; var searchMethods = new List <SagaSearchMethod>(); SetSearchMethodForMappings(correlator, sagaType, sagaDataType, associatedMessages, searchMethods); EnsureMappingToStartMessage(correlator, sagaType, associatedMessages); return(new SagaMetadata(sagaType, sagaDataType, correlationProperty, associatedMessages, searchMethods)); }
protected override void CorrelateUsing(SagaCorrelator <TData> correlator) { Contract.Requires <ArgumentNullException>(correlator != null, nameof(correlator)); }
protected override void CorrelateUsing( SagaCorrelator<ProcurementData> correlator ) { correlator.Correlate<SubmitPurchaseOrder>( command => command.AggregateId ).To( saga => saga.OrderId ); correlator.Correlate<SalesOrderReceived>( @event => @event.AggregateId ).To( saga => saga.OrderId ); correlator.Correlate<ShipmentReceived>( @event => @event.AggregateId ).To( saga => saga.OrderId ); }