I

namespace Library
{
    using Automatonymous;
    using MassTransit;
    using MassTransit.EntityFrameworkCoreIntegration;
    using MassTransit.EntityFrameworkCoreIntegration.Mappings;
    using MassTransit.ExtensionsDependencyInjectionIntegration;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Routing;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore.Metadata.Builders;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(host => host.UseStartup<Startup>());
    }
    public class Startup
    {
        public IConfiguration Configuration { get; }
        public Startup(IConfiguration configuration) => Configuration = configuration;
        public void ConfigureServices(IServiceCollection svcs)
        {
            svcs.AddDbContext<LibraryContext>(opts => opts
                .UseSqlServer(Configuration.GetConnectionString("Library")));
            svcs.AddMassTransit(MassTransitConfigure);
            svcs.AddMassTransitHostedService();
            svcs.AddControllers();
        }
        public void Configure(IApplicationBuilder budr)
        {
            budr.UseRouting();
            budr.UseEndpoints(rute => rute.MapControllers());
        }
        public void MassTransitConfigure(IServiceCollectionBusConfigurator cfgr)
        {
            cfgr.AddSagaStateMachine<BookStateMachine, Book>()
                .EntityFrameworkRepository(cfg => cfg.ExistingDbContext<LibraryContext>());

            cfgr.AddSagaStateMachine<ReserveStateMachine, Reserve>()
                .EntityFrameworkRepository(cfg => cfg.ExistingDbContext<LibraryContext>());

            cfgr.UsingRabbitMq((ctx, cfg) =>
            {
                cfg.Host("localhost", "/", hst =>
                {
                    hst.Username("guest");
                    hst.Password("guest");
                });
                cfg.ConfigureEndpoints(ctx);
            });
        }
    }

    public class LibraryContext : SagaDbContext
    {
        public LibraryContext(DbContextOptions<LibraryContext> options) : base(options) { }
        public DbSet<Book> Books { get; set; }
        public DbSet<Reserve> Reserves { get; set; }
        protected override IEnumerable<ISagaClassMap> Configurations
        {
            get
            {
                yield return new BookMap();
                yield return new ReserveMap();
            }
        }
    }

    public class Book : SagaStateMachineInstance
    {
        public Guid CorrelationId { get; set; }
        public int State { get; set; }
        public DateTime Timestamp { get; set; }
    }
    public class BookMap : SagaClassMap<Book>
    {
        protected override void Configure(EntityTypeBuilder<Book> entity, ModelBuilder model)
        {
            base.Configure(entity, model);
            entity.ToTable(nameof(Book));
        }
    }
    public class BookStateMachine : MassTransitStateMachine<Book>
    {
        public ILogger<BookStateMachine> Logger { get; }
        public BookStateMachine(ILogger<BookStateMachine> logger)
        {
            Logger = logger;

            InstanceState(ins => ins.State, Created, Reserved);

            Event(() => BookCreated, cfg => cfg.CorrelateById(ctx => ctx.Message.BookId));

            Event(() => ReserveCreated, cfg => cfg.CorrelateById(ctx => ctx.Message.BookId));

            Initially(
                When(BookCreated)
                    .Then(ctx =>
                    {
                        ctx.Instance.Timestamp = ctx.Data.Timestamp;
                    })
                    .TransitionTo(Created));

            During(Created,
                When(ReserveCreated)
                    .TransitionTo(Reserved)
                    .PublishAsync(ctx => ctx.Init<BookReserved>(new
                    {
                        ReserveId = ctx.Data.ReserveId,
                        Timestamp = ctx.Data.Timestamp,
                        BookId = ctx.Data.BookId // ctx.Instance.CorrelationId
                    })));
        }
        public State Created { get; }
        public State Reserved { get; }
        public Event<BookCreated> BookCreated { get; }
        public Event<ReserveCreated> ReserveCreated { get; }
    }
    public class BookController : ControllerBase
    {
        public ILogger<BookController> Logger { get; }
        public IPublishEndpoint PublishEndpoint { get; }
        public BookController(
            ILogger<BookController> logger,
            IPublishEndpoint publishEndpoint)
        {
            Logger = logger;
            PublishEndpoint = publishEndpoint;
        }

        [HttpGet, Route("api/books/create/{bookId}")]
        public async Task<IActionResult> CreateAsync(Guid bookId)
        {
            await PublishEndpoint.Publish<BookCreated>(new
            {
                BookId = bookId,
                Timestamp = InVar.Timestamp
            });
            return Ok(bookId);
        }
    }

    public interface BookCreated
    {
        Guid BookId { get; }
        DateTime Timestamp { get; }
    }
    public interface ReserveCreated
    {
        Guid ReserveId { get; }
        DateTime Timestamp { get; }
        Guid BookId { get; }
    }
    public interface BookReserved
    {
        Guid ReserveId { get; }
        DateTime Timestamp { get; }
        Guid BookId { get; }
    }

    public class Reserve : SagaStateMachineInstance
    {
        public Guid CorrelationId { get; set; }
        public int State { get; set; }
        public DateTime Timestamp { get; set; }
        public Guid BookId { get; set; }
    }
    public class ReserveMap : SagaClassMap<Reserve>
    {
        protected override void Configure(EntityTypeBuilder<Reserve> entity, ModelBuilder model)
        {
            base.Configure(entity, model);
            entity.ToTable(nameof(Reserve));
        }
    }
    public class ReserveStateMachine : MassTransitStateMachine<Reserve>
    {
        public ILogger<ReserveStateMachine> Logger { get; }
        public ReserveStateMachine(ILogger<ReserveStateMachine> logger)
        {
            Logger = logger;

            InstanceState(ins => ins.State, Created, Reserved);

            Event(() => ReserveCreated, cfg => cfg.CorrelateById(ctx => ctx.Message.ReserveId));

            Event(() => BookReserved, cfg => cfg.CorrelateById(ctx => ctx.Message.ReserveId));

            Initially(
                When(ReserveCreated)
                    .Then(ctx =>
                    {
                        ctx.Instance.Timestamp = ctx.Data.Timestamp;
                        ctx.Instance.BookId = ctx.Data.BookId;
                    })
                    .TransitionTo(Created));

            During(Created,
                When(BookReserved)
                    .TransitionTo(Reserved));
        }
        public State Created { get; }
        public State Reserved { get; }
        public Event<ReserveCreated> ReserveCreated { get; }
        public Event<BookReserved> BookReserved { get; }
    }
    public class ReserveController : ControllerBase
    {
        public ILogger<ReserveController> Logger { get; }
        public IPublishEndpoint PublishEndpoint { get; }
        public ReserveController(
            ILogger<ReserveController> logger,
            IPublishEndpoint publishEndpoint)
        {
            Logger = logger;
            PublishEndpoint = publishEndpoint;
        }

        [HttpGet, Route("api/reserves/create/{reserveId}/{bookId}")]
        public async Task<IActionResult> Create(Guid reserveId, Guid bookId)
        {
            await PublishEndpoint.Publish<ReserveCreated>(new
            {
                ReserveId = reserveId,
                Timestamp = InVar.Timestamp,
                BookId = bookId
            });
            return Ok(reserveId);
        }
    }
}
原文地址:https://www.cnblogs.com/xiaowangzhi/p/14328659.html