From 91cbdd4e2accb1d0555b2948cde43153757b4a73 Mon Sep 17 00:00:00 2001 From: Chris Patterson Date: Thu, 24 Jan 2019 16:28:58 -0600 Subject: [PATCH] Building a new sample showing how to use various containers with MassTransit --- .gitignore | 63 +++++++++ Sample-Autofac/Program.cs | 121 ++++++++++++++++++ Sample-Autofac/Sample-Autofac.csproj | 19 +++ Sample-Containers.sln | 28 ++++ Sample.Components/OrderState.cs | 15 +++ Sample.Components/OrderStateAuditConsumer.cs | 16 +++ Sample.Components/OrderStateMachine.cs | 33 +++++ .../PublishOrderEventActivity.cs | 70 ++++++++++ Sample.Components/Sample.Components.csproj | 16 +++ Sample.Components/SubmitOrderConsumer.cs | 18 +++ Sample.Contracts/OrderReceived.cs | 10 ++ Sample.Contracts/OrderStateCreated.cs | 10 ++ Sample.Contracts/Sample.Contracts.csproj | 7 + Sample.Contracts/SubmitOrder.cs | 10 ++ 14 files changed, 436 insertions(+) create mode 100644 .gitignore create mode 100644 Sample-Autofac/Program.cs create mode 100644 Sample-Autofac/Sample-Autofac.csproj create mode 100644 Sample-Containers.sln create mode 100644 Sample.Components/OrderState.cs create mode 100644 Sample.Components/OrderStateAuditConsumer.cs create mode 100644 Sample.Components/OrderStateMachine.cs create mode 100644 Sample.Components/PublishOrderEventActivity.cs create mode 100644 Sample.Components/Sample.Components.csproj create mode 100644 Sample.Components/SubmitOrderConsumer.cs create mode 100644 Sample.Contracts/OrderReceived.cs create mode 100644 Sample.Contracts/OrderStateCreated.cs create mode 100644 Sample.Contracts/Sample.Contracts.csproj create mode 100644 Sample.Contracts/SubmitOrder.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb55ea9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,63 @@ +build_output/* +build_artifacts/* +build_temp/* +*.suo +*.user +packages +*.dotCover + +*.ncrunch* +.vs + +.fake + +src/logs/* + +**/*.sln* +bin +obj +_ReSharper* + +*.csproj.user +*.resharper.user +*.resharper +*.ReSharper +*.cache +*~ +*.swp +*.bak +*.orig + +NuGet.exe +packages + +# Tests +TestResult.xml +submit.xml +tests/* +SolutionVersion.cs +src/SolutionVersion.cs +tests +doc/build/* +*.[lm]df +*.runsettings + +# osx noise +.DS_Store +*.DotSettings +/src/MassTransit.SimpleInjectorIntegration/MassTransit.SimpleInjectorIntegration.csproj.nuspec +*.DS_Store + +_book +/node_modules +.vscode +**/.idea/ +appsettings.Development.json + +# Cake +tools/** +!tools/packages.config + +# Artifacts +artifacts/** +artifacts_old/** \ No newline at end of file diff --git a/Sample-Autofac/Program.cs b/Sample-Autofac/Program.cs new file mode 100644 index 0000000..d1041ab --- /dev/null +++ b/Sample-Autofac/Program.cs @@ -0,0 +1,121 @@ +using System; +using System.Threading.Tasks; +using Autofac; +using GreenPipes; +using MassTransit; +using MassTransit.Saga; +using MassTransit.Util; +using Sample.Components; +using Sample.Contracts; + +namespace Sample_Autofac +{ + class Program + { + static void Main() + { + var container = ConfigureContainer(); + + var bus = container.Resolve(); + + try + { + bus.Start(); + try + { + Console.WriteLine("Bus started, type 'exit' to exit."); + + bool running = true; + while (running) + { + var input = Console.ReadLine(); + switch (input) + { + case "exit": + case "quit": + running = false; + break; + + case "submit": + TaskUtil.Await(() => Submit(container)); + break; + } + } + } + finally + { + bus.Stop(); + } + } + catch (Exception exception) + { + Console.WriteLine(exception); + } + } + + static async Task Submit(IContainer container) + { + ISendEndpointProvider provider = container.Resolve(); + var endpoint = await provider.GetSendEndpoint(new Uri("loopback://localhost/submit-order")); + + await endpoint.Send(new + { + OrderId = NewId.NextGuid(), + OrderDateTime = DateTimeOffset.Now + }, sendContext => sendContext.CorrelationId = NewId.NextGuid()); + } + + static IContainer ConfigureContainer() + { + var builder = new ContainerBuilder(); + + builder.RegisterConsumers(typeof(SubmitOrderConsumer).Assembly) + .InstancePerLifetimeScope(); + + builder.RegisterStateMachineSagas(typeof(OrderStateMachine).Assembly); + builder.RegisterType(); + + builder.RegisterGeneric(typeof(InMemorySagaRepository<>)) + .As(typeof(ISagaRepository<>)); + + builder.Register(BusFactory) + .As() + .As() + .As() + .As() + .SingleInstance(); + + return builder.Build(); + } + + static IBusControl BusFactory(IComponentContext context) + { + return Bus.Factory.CreateUsingInMemory(cfg => + { + cfg.ReceiveEndpoint("submit-order", e => + { + e.UseMessageRetry(r => r.Interval(5, 1000)); + e.UseInMemoryOutbox(); + + e.Consumer(context); + }); + + cfg.ReceiveEndpoint("order-state", e => + { + e.UseMessageRetry(r => r.Interval(5, 1000)); + e.UseInMemoryOutbox(); + + e.StateMachineSaga(context); + }); + + cfg.ReceiveEndpoint("order-state-audit", e => + { + e.UseMessageRetry(r => r.Interval(5, 1000)); + e.UseInMemoryOutbox(); + + e.Consumer(context); + }); + }); + } + } +} \ No newline at end of file diff --git a/Sample-Autofac/Sample-Autofac.csproj b/Sample-Autofac/Sample-Autofac.csproj new file mode 100644 index 0000000..8c65008 --- /dev/null +++ b/Sample-Autofac/Sample-Autofac.csproj @@ -0,0 +1,19 @@ + + + + Exe + netcoreapp2.2 + Sample_Autofac + + + + + + + + + + + + + diff --git a/Sample-Containers.sln b/Sample-Containers.sln new file mode 100644 index 0000000..78751a1 --- /dev/null +++ b/Sample-Containers.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample-Autofac", "Sample-Autofac\Sample-Autofac.csproj", "{459F5E30-8F4A-4A0A-9E9E-D4F08180EA59}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Contracts", "Sample.Contracts\Sample.Contracts.csproj", "{455F8888-B3CB-4467-B3E6-D3F6E0B3FBBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Components", "Sample.Components\Sample.Components.csproj", "{CF024BAF-1C16-4BA7-B323-350FC2EFA85B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {459F5E30-8F4A-4A0A-9E9E-D4F08180EA59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {459F5E30-8F4A-4A0A-9E9E-D4F08180EA59}.Debug|Any CPU.Build.0 = Debug|Any CPU + {459F5E30-8F4A-4A0A-9E9E-D4F08180EA59}.Release|Any CPU.ActiveCfg = Release|Any CPU + {459F5E30-8F4A-4A0A-9E9E-D4F08180EA59}.Release|Any CPU.Build.0 = Release|Any CPU + {455F8888-B3CB-4467-B3E6-D3F6E0B3FBBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {455F8888-B3CB-4467-B3E6-D3F6E0B3FBBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {455F8888-B3CB-4467-B3E6-D3F6E0B3FBBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {455F8888-B3CB-4467-B3E6-D3F6E0B3FBBC}.Release|Any CPU.Build.0 = Release|Any CPU + {CF024BAF-1C16-4BA7-B323-350FC2EFA85B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CF024BAF-1C16-4BA7-B323-350FC2EFA85B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CF024BAF-1C16-4BA7-B323-350FC2EFA85B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CF024BAF-1C16-4BA7-B323-350FC2EFA85B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Sample.Components/OrderState.cs b/Sample.Components/OrderState.cs new file mode 100644 index 0000000..68963b5 --- /dev/null +++ b/Sample.Components/OrderState.cs @@ -0,0 +1,15 @@ +using System; +using Automatonymous; + +namespace Sample.Components +{ + public class OrderState : + SagaStateMachineInstance + { + public Guid CorrelationId { get; set; } + public string CurrentState { get; set; } + + public DateTimeOffset OrderSubmissionDateTime { get; set; } + public DateTime OrderSubmissionDateTimeUtc { get; set; } + } +} \ No newline at end of file diff --git a/Sample.Components/OrderStateAuditConsumer.cs b/Sample.Components/OrderStateAuditConsumer.cs new file mode 100644 index 0000000..40b4df8 --- /dev/null +++ b/Sample.Components/OrderStateAuditConsumer.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading.Tasks; +using MassTransit; +using Sample.Contracts; + +namespace Sample.Components +{ + public class OrderStateAuditConsumer : + IConsumer + { + public async Task Consume(ConsumeContext context) + { + await Console.Out.WriteLineAsync($"OrderState(created): {context.Message.OrderId} ({context.ConversationId})"); + } + } +} \ No newline at end of file diff --git a/Sample.Components/OrderStateMachine.cs b/Sample.Components/OrderStateMachine.cs new file mode 100644 index 0000000..c69208a --- /dev/null +++ b/Sample.Components/OrderStateMachine.cs @@ -0,0 +1,33 @@ +using System; +using Automatonymous; +using Sample.Contracts; + +namespace Sample.Components +{ + public class OrderStateMachine : + MassTransitStateMachine + { + public OrderStateMachine() + { + Event(() => OrderReceived, x => x.CorrelateById(m => m.Message.OrderId)); + + InstanceState(x => x.CurrentState); + + Initially( + When(OrderReceived) + .ThenAsync(context => Console.Out.WriteLineAsync($"OrderState: Order Received: {context.Data.OrderId}")) + .Then((context) => + { + context.Instance.OrderSubmissionDateTime = context.Data.OrderDateTime; + context.Instance.OrderSubmissionDateTimeUtc = context.Data.OrderDateTime.DateTime.ToUniversalTime(); + }) + .Activity(s => s.OfInstanceType()) + .TransitionTo(Submitted)); + } + + public Event OrderReceived { get; private set; } + + public State Submitted { get; private set; } + public State Accepted { get; private set; } + } +} \ No newline at end of file diff --git a/Sample.Components/PublishOrderEventActivity.cs b/Sample.Components/PublishOrderEventActivity.cs new file mode 100644 index 0000000..0c66809 --- /dev/null +++ b/Sample.Components/PublishOrderEventActivity.cs @@ -0,0 +1,70 @@ +using System; +using System.Threading.Tasks; +using Automatonymous; +using GreenPipes; +using MassTransit; +using Sample.Contracts; + +namespace Sample.Components +{ + public class PublishOrderEventActivity : + Activity + { + readonly IPublishEndpoint _publishEndpoint; + + public PublishOrderEventActivity(IPublishEndpoint publishEndpoint) + { + _publishEndpoint = publishEndpoint; + } + + public void Probe(ProbeContext context) + { + context.CreateScope("publish-order-event"); + } + + public void Accept(StateMachineVisitor visitor) + { + visitor.Visit(this); + } + + public async Task Execute(BehaviorContext context, Behavior next) + { + await Console.Out.WriteLineAsync( + $"Publishing Order Event Created: {context.Instance.CorrelationId} ({context.GetPayload().ConversationId})"); + + await _publishEndpoint.Publish(new + { + OrderId = context.Instance.CorrelationId, + Timestamp = DateTime.UtcNow + }, sendContext => sendContext.CorrelationId = NewId.NextGuid()); + + await next.Execute(context); + } + + public async Task Execute(BehaviorContext context, Behavior next) + { + await Console.Out.WriteLineAsync( + $"Publishing Order Event Created: {context.Instance.CorrelationId} ({context.GetPayload().ConversationId})"); + + await _publishEndpoint.Publish(new + { + OrderId = context.Instance.CorrelationId, + Timestamp = DateTime.UtcNow + }, sendContext => sendContext.CorrelationId = NewId.NextGuid()); + + await next.Execute(context); + } + + public Task Faulted(BehaviorExceptionContext context, Behavior next) + where TException : Exception + { + return next.Faulted(context); + } + + public Task Faulted(BehaviorExceptionContext context, Behavior next) + where TException : Exception + { + return next.Faulted(context); + } + } +} \ No newline at end of file diff --git a/Sample.Components/Sample.Components.csproj b/Sample.Components/Sample.Components.csproj new file mode 100644 index 0000000..2acc61c --- /dev/null +++ b/Sample.Components/Sample.Components.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/Sample.Components/SubmitOrderConsumer.cs b/Sample.Components/SubmitOrderConsumer.cs new file mode 100644 index 0000000..2658f45 --- /dev/null +++ b/Sample.Components/SubmitOrderConsumer.cs @@ -0,0 +1,18 @@ +using System; +using System.Threading.Tasks; +using MassTransit; +using Sample.Contracts; + +namespace Sample.Components +{ + public class SubmitOrderConsumer : + IConsumer + { + public async Task Consume(ConsumeContext context) + { + await Console.Out.WriteLineAsync($"Submit Order Consumer: {context.Message.OrderId} ({context.ConversationId})"); + + await context.Publish(context.Message, sendContext => sendContext.CorrelationId = NewId.NextGuid()); + } + } +} \ No newline at end of file diff --git a/Sample.Contracts/OrderReceived.cs b/Sample.Contracts/OrderReceived.cs new file mode 100644 index 0000000..602f64c --- /dev/null +++ b/Sample.Contracts/OrderReceived.cs @@ -0,0 +1,10 @@ +using System; + +namespace Sample.Contracts +{ + public interface OrderReceived + { + Guid OrderId { get; } + DateTimeOffset OrderDateTime { get; } + } +} \ No newline at end of file diff --git a/Sample.Contracts/OrderStateCreated.cs b/Sample.Contracts/OrderStateCreated.cs new file mode 100644 index 0000000..e1c0fc7 --- /dev/null +++ b/Sample.Contracts/OrderStateCreated.cs @@ -0,0 +1,10 @@ +using System; + +namespace Sample.Contracts +{ + public interface OrderStateCreated + { + Guid OrderId { get; } + DateTime Timestamp { get; } + } +} \ No newline at end of file diff --git a/Sample.Contracts/Sample.Contracts.csproj b/Sample.Contracts/Sample.Contracts.csproj new file mode 100644 index 0000000..dbdcea4 --- /dev/null +++ b/Sample.Contracts/Sample.Contracts.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/Sample.Contracts/SubmitOrder.cs b/Sample.Contracts/SubmitOrder.cs new file mode 100644 index 0000000..fc9a803 --- /dev/null +++ b/Sample.Contracts/SubmitOrder.cs @@ -0,0 +1,10 @@ +using System; + +namespace Sample.Contracts +{ + public interface SubmitOrder + { + Guid OrderId { get; } + DateTimeOffset OrderDateTime { get; } + } +} \ No newline at end of file