/// <summary> /// Return all saga types that are initiated by this type of message /// </summary> /// <param name="message">Initialisation message to check for</param> /// <param name="assemblies">Assemblies to scan for sagas</param> /// <returns></returns> internal static IEnumerable <Type> GetSagaTypesInitiatedBy(IInitiatingSagaMessage message, params Assembly[] assemblies) { try { if (assemblies.Length == 0) { assemblies = AppDomain.CurrentDomain.GetAssemblies(); } var messageType = message.GetType(); var initiatingInterfaceType = typeof(InitiatedBy <>).MakeGenericType(messageType); var scan = assemblies.SelectMany(a => a.GetTypes()) .Where(t => initiatingInterfaceType.IsAssignableFrom(t)) .ToList(); return(scan); } catch (ReflectionTypeLoadException ex) { //Display or log the error based on your application. throw new Exception(BuildFusionException(ex)); } }
public IAccessibleSaga ResolveSagaInititatedBy(IInitiatingSagaMessage message) { var interfaceType = typeof(InitiatedBy <>).MakeGenericType(message.GetType()); var saga = container.Resolve(interfaceType); return((IAccessibleSaga)saga); }
public OperationResult Consume(IInitiatingSagaMessage initiatingMessage) { if (initiatingMessage.CorrelationId == default(Guid)) { throw new ArgumentException("CorrelationId was not provided in the message. Please make sure you assign CorrelationId before initiating your Saga"); } // find all sagas that can be initiated by this message var sagaTypes = Reflection.GetSagaTypesInitiatedBy(initiatingMessage, assembliesToScan); if (!sagaTypes.Any()) { throw new ArgumentException($"Message of type {initiatingMessage.GetType().Name} is not initiating any Sagas. Please add InitiatedBy<{initiatingMessage.GetType().Name}> to your Saga type"); } if (sagaTypes.Count() > 1) { // can't have multiple sagas initiated by the same message - can't have 2 sagas of different types with the same CorrelationId var sagaNames = String.Join(", ", sagaTypes.Select(t => t.Name)); throw new ArgumentException($"Message of type {initiatingMessage.GetType().Name} is initiating more than one saga. Please make sure any single message is initiating only one saga. Affected sagas: {sagaNames}"); } var sagaType = sagaTypes.First(); // try to find sagas that already exist var existingSaga = Reflection.InvokeGenericMethod(sagaRepository, "Find", sagaType, initiatingMessage.CorrelationId); if (existingSaga != null) { throw new ArgumentException($"Trying to initiate the same saga twice. {initiatingMessage.GetType().Name} is Initiating Message, but saga of type {sagaType.Name} with CorrelationId {initiatingMessage.CorrelationId} already exists"); } // now create an instance of saga and persist the data var saga = serviceLocator.Resolve(sagaType); Reflection.Set(saga, "CorrelationId", initiatingMessage.CorrelationId); // if SagaData is null - create an instance of the object and assign to saga var sagaData = Reflection.Get(saga, "SagaData"); if (sagaData == null) { var sagaDataType = Reflection.GetInterfaceGenericType(saga, typeof(ISaga <>)); var newSagaData = Activator.CreateInstance(sagaDataType); Reflection.Set(saga, "SagaData", newSagaData); } var sagaHeaders = Reflection.Get(saga, "Headers"); if (sagaHeaders == null) { Reflection.Set(saga, "Headers", new Dictionary <String, String>()); } var errors = (OperationResult)Reflection.InvokeMethod(saga, "Initiate", initiatingMessage); sagaRepository.Save(saga); // now the real question - should we persist the saga if operation returned errors? Probably should be configurable return(errors); }
public OperationResult Consume(IInitiatingSagaMessage sagaMessage) { var opResult = _sagaMediator.Consume(sagaMessage); if (opResult.HasErrors) { Errors.AddCollection(opResult.Errors); } return(opResult); }
/// <summary> /// Consumes the specified initiating message and creates a new instance of the correlating saga. /// <para>Saga is not persisted if operation have failed</para> /// </summary> /// <param name="initiatingMessage">The initiating message.</param> /// <returns> /// Result of the operation /// </returns> /// <exception cref="System.ArgumentException"></exception> public OperationResult Consume(IInitiatingSagaMessage initiatingMessage) { Guard.CheckSagaMessage(initiatingMessage, nameof(initiatingMessage)); var resolvedSaga = sagaFactory.ResolveSagaInititatedBy(initiatingMessage); var sagaType = resolvedSaga.GetType(); // try to find sagas that already exist var existingSaga = NSagaReflection.InvokeGenericMethod(sagaRepository, "Find", sagaType, initiatingMessage.CorrelationId); if (existingSaga != null) { throw new ArgumentException($"Trying to initiate the same saga twice. {initiatingMessage.GetType().Name} is Initiating Message, but saga of type {sagaType.Name} with CorrelationId {initiatingMessage.CorrelationId} already exists"); } // now create an instance of saga and persist the data var saga = sagaFactory.ResolveSaga(sagaType); NSagaReflection.Set(saga, "CorrelationId", initiatingMessage.CorrelationId); // if SagaData is null - create an instance of the object and assign to saga var sagaData = NSagaReflection.Get(saga, "SagaData"); if (sagaData == null) { var sagaDataType = NSagaReflection.GetInterfaceGenericType(saga, typeof(ISaga <>)); var newSagaData = Activator.CreateInstance(sagaDataType); NSagaReflection.Set(saga, "SagaData", newSagaData); } var sagaHeaders = NSagaReflection.Get(saga, "Headers"); if (sagaHeaders == null) { NSagaReflection.Set(saga, "Headers", new Dictionary <String, String>()); } pipelineHook.BeforeInitialisation(new PipelineContext(initiatingMessage, (IAccessibleSaga)saga)); var errors = (OperationResult)NSagaReflection.InvokeMethod(saga, "Initiate", initiatingMessage); pipelineHook.AfterInitialisation(new PipelineContext(initiatingMessage, (IAccessibleSaga)saga, errors)); if (errors.IsSuccessful) { sagaRepository.Save(saga); pipelineHook.AfterSave(new PipelineContext(initiatingMessage, (IAccessibleSaga)saga, errors)); } return(errors); }
/// <summary> /// Return all saga types that are initiated by this type of message /// </summary> /// <param name="message">Initialisation message to check for</param> /// <param name="assemblies">Assemblies to scan for sagas</param> /// <returns></returns> public static IEnumerable <Type> GetSagaTypesInitiatedBy(IInitiatingSagaMessage message, params Assembly[] assemblies) { if (assemblies.Length == 0) { assemblies = AppDomain.CurrentDomain.GetAssemblies(); } var messageType = message.GetType(); var initiatingInterfaceType = typeof(InitiatedBy <>).MakeGenericType(messageType); var scan = assemblies.SelectMany(a => a.GetTypes()) .Where(t => initiatingInterfaceType.IsAssignableFrom(t)) .ToList(); return(scan); }
public IAccessibleSaga ResolveSagaInititatedBy(IInitiatingSagaMessage message) { throw new NotImplementedException(); }