/// <summary> /// Finds and returns saga instance with the given correlation ID. /// Actually creates an instance of saga from Saga factory, retrieves SagaData and Headers from the storage and populates the instance with these. /// </summary> /// <typeparam name="TSaga">Type of saga we are looking for</typeparam> /// <param name="correlationId">CorrelationId to identify the saga</param> /// <returns> /// An instance of the saga. Or Null if there is no saga with this ID. /// </returns> public TSaga Find <TSaga>(Guid correlationId) where TSaga : class, IAccessibleSaga { var sagasTable = GetSagaTable(); var retrieveOperation = TableOperation.Retrieve <StorageModel>(PartitionKey, correlationId.ToString()); var retrieveResult = sagasTable.Execute(retrieveOperation); if (retrieveResult.Result == null) { return(null); } var storedModel = (StorageModel)retrieveResult.Result; var sagaDataType = NSagaReflection.GetInterfaceGenericType <TSaga>(typeof(ISaga <>)); var sagaData = messageSerialiser.Deserialise(storedModel.BlobData, sagaDataType); var sagaInstance = sagaFactory.ResolveSaga <TSaga>(); sagaInstance.CorrelationId = correlationId; sagaInstance.Headers = messageSerialiser.Deserialise <Dictionary <String, String> >(storedModel.Headers); NSagaReflection.Set(sagaInstance, "SagaData", sagaData); return(sagaInstance); }
/// <summary> /// Extension method to execute default NSaga components registration in Autofac Container Builder. /// <para> /// Default registrations are: /// <list type="bullet"> /// <item><description><see cref="JsonNetSerialiser"/> to serialise messages; </description></item> /// <item><description><see cref="InMemorySagaRepository"/> to store saga datas; </description></item> /// <item><description><see cref="AutofacSagaFactory"/> to resolve instances of Sagas;</description></item> /// <item><description><see cref="SagaMetadata"/> to work as the key component - SagaMediator;</description></item> /// <item><description><see cref="MetadataPipelineHook"/> added to the pipeline to preserve metadata about incoming messages.</description></item> /// </list> /// </para> /// </summary> /// <param name="builder">Container Builder to do the registration</param> /// <param name="assemblies">Assemblies to scan for Sagas</param> /// <returns>The same container builder so the calls can be chained in Builder-fashion</returns> public static ContainerBuilder RegisterNSagaComponents(this ContainerBuilder builder, params Assembly[] assemblies) { Guard.ArgumentIsNotNull(builder, nameof(builder)); Guard.ArgumentIsNotNull(assemblies, nameof(assemblies)); builder.RegisterType <AutofacSagaFactory>().As <ISagaFactory>(); builder.RegisterType <JsonNetSerialiser>().As <IMessageSerialiser>(); builder.RegisterType <InMemorySagaRepository>().As <ISagaRepository>(); builder.RegisterType <SagaMediator>().As <ISagaMediator>(); builder.RegisterType <MetadataPipelineHook>().As <IPipelineHook>(); var sagatTypesDefinitions = NSagaReflection.GetAllSagasInterfaces(assemblies); foreach (var sagatTypesDefinition in sagatTypesDefinitions) { builder.RegisterType(sagatTypesDefinition.Key).As(sagatTypesDefinition.Value); } var allSagaTypes = NSagaReflection.GetAllSagaTypes(assemblies); builder.RegisterTypes(allSagaTypes.ToArray()); return(builder); }
public void GetSagaTypes_Always_ContainsMySaga() { // Act var result = NSagaReflection.GetAllSagaTypes(new Assembly[] { typeof(NSagaReflectionTests).Assembly }); // Assert result.Should().Contain(s => s == typeof(MySaga)) .And.Contain(s => s == typeof(SagaWithErrors)) .And.HaveCount(2); }
public void GetSagaTypesConsuming_Returns_MySaga() { //Arrange var consumedMessage = new MySagaConsumingMessage(); // Act var result = NSagaReflection.GetSagaTypesConsuming(consumedMessage, typeof(NSagaReflectionTests).Assembly); // Assert result.Should().HaveCount(1).And.Contain(typeof(MySaga)); }
public void GetSagaTypesInitiatedBy_Returns_MySaga() { // Arrange var initiatingMessage = new MySagaInitiatingMessage(); // Act var result = NSagaReflection.GetSagaTypesInitiatedBy(initiatingMessage, typeof(NSagaReflectionTests).Assembly); // Assert result.Should().HaveCount(1).And.Contain(typeof(MySaga)); }
public void InvokeMethod_OverloadInt_CallsTheCorrectOverload() { //Arrange var testSubject = new MyReflectionTestSubject(); var expectedInt = 42; // Act NSagaReflection.InvokeMethod(testSubject, "Overload", expectedInt); // Assert testSubject.OverloadInt.Should().Be(expectedInt); }
public void InvokeMethod_OverloadString_CallsTheCorrectOverload() { //Arrange var testSubject = new MyReflectionTestSubject(); var expectedString = Guid.NewGuid().ToString(); // Act NSagaReflection.InvokeMethod(testSubject, "Overload", expectedString); // Assert testSubject.OverloadString.Should().Be(expectedString); }
public void InvokeMethod_Does_NotThrow() { //Arrange var testSubject = new MyReflectionTestSubject(); var expected = Guid.NewGuid(); // Act NSagaReflection.InvokeMethod(testSubject, "Initialise", expected); // Assert testSubject.Id.Should().Be(expected); }
public void InvokeGenericMethod_Does_NotThrow() { // Arrange var sagaType = NSagaReflection.GetSagaTypesInitiatedBy(new MySagaInitiatingMessage()).First(); // there could be only one! var spy = new RepositorySpy(); var repository = new MyStubRepository(spy); var expectedGuid = Guid.NewGuid(); // Act NSagaReflection.InvokeGenericMethod(repository, "Find", sagaType, expectedGuid); // Assert spy.Find.Should().Be(expectedGuid); }
/// <summary> /// Persists the instance of saga into the database storage. /// Actually stores SagaData and Headers. All other variables in saga are not persisted /// </summary> /// <typeparam name="TSaga">Type of saga</typeparam> /// <param name="saga">Saga instance</param> public void Save <TSaga>(TSaga saga) where TSaga : class, IAccessibleSaga { var sagasTable = GetSagaTable(); var sagaData = NSagaReflection.Get(saga, "SagaData"); var serialisedData = messageSerialiser.Serialise(sagaData); var storageModel = new StorageModel() { RowKey = saga.CorrelationId.ToString(), Headers = messageSerialiser.Serialise(saga.Headers), BlobData = serialisedData, }; var insertOperation = TableOperation.InsertOrReplace(storageModel); sagasTable.Execute(insertOperation); }
/// <summary> /// Extension method to execute default NSaga components registration in StructureMap Container Builder. /// <para> /// Default registrations are: /// <list type="bullet"> /// <item><description><see cref="JsonNetSerialiser"/> to serialise messages; </description></item> /// <item><description><see cref="InMemorySagaRepository"/> to store saga datas; </description></item> /// <item><description><see cref="StructureMapSagaFactory"/> to resolve instances of Sagas;</description></item> /// <item><description><see cref="SagaMetadata"/> to work as the key component - SagaMediator;</description></item> /// <item><description><see cref="MetadataPipelineHook"/> added to the pipeline to preserve metadata about incoming messages.</description></item> /// </list> /// </para> /// </summary> /// <param name="builder">Container Builder to do the registration</param> /// <param name="assemblies">Assemblies to scan for Sagas</param> /// <returns>The same container builder so the calls can be chained in Builder-fashion</returns> public static Container RegisterNSagaComponents(this Container builder, params Assembly[] assemblies) { Guard.ArgumentIsNotNull(builder, nameof(builder)); Guard.ArgumentIsNotNull(assemblies, nameof(assemblies)); builder.Configure(x => { x.AddRegistry <NSagaRegistry>(); var sagatTypesDefinitions = NSagaReflection.GetAllSagasInterfaces(assemblies); foreach (var sagatTypesDefinition in sagatTypesDefinitions) { x.AddType(sagatTypesDefinition.Value, sagatTypesDefinition.Key.UnderlyingSystemType); } var allSagaTypes = NSagaReflection.GetAllSagaTypes(assemblies).ToList(); allSagaTypes.ToList().ForEach(t => { x.For(t); }); }); return(builder); }