同样地,请将PassThroughEventBus实现在另一个NetStandard的Class Library中,虽然它不需要额外的依赖,但它毕竟是众多消息总线中的一种,将它从接口定义的程序集中剥离开来,好处有两点:第一,保证了定义接口的程序集的纯净度,使得该程序集不需要依赖任何外部组件,并确保了该程序集的职责单一性,即为消息系统的实现提供基础类库;第二,将PassThroughEventBus置于独立的程序集中,有利于调用方针对IEventBus进行技术选择,比如,如果开发者选择使用基于RabbitMQ的实现,那么,只需要引用基于RabbitMQ实现IEventBus接口的程序集就可以了,而无需引用包含了PassThroughEventBus的程序集。这一点我觉得可以归纳为框架设计中“隔离依赖关系(Dependency Segregation)”的准则。
好了,基本组件都定义好了,接下来,让我们一起基于ASP.NET Core Web API来做一个RESTful服务,并接入上面的消息总线机制,实现消息的派发和订阅。
Customer RESTful API我们仍然以客户管理的RESTful API为例子,不过,我们不会过多地讨论如何去实现管理客户信息的RESTful服务,那并不是本文的重点。作为一个案例,我使用ASP.NET Core 2.0 Web API建立了这个服务,使用Visual Studio 2017 15.5做开发,并在CustomersController中使用Dapper来对客户信息CRUD。后台基于SQL Server 2017 Express Edition,使用SQL Server Management Studio能够让我方便地查看数据库操作的结果。
RESTful API的实现假设我们的客户信息只包含客户ID和名称,下面的CustomersController代码展示了我们的RESTful服务是如何保存并读取客户信息的。当然,我已经将本文的代码通过Github开源,开源协议为MIT,虽然商业友好,但毕竟是案例代码没有经过测试,所以请谨慎使用。本文源代码的使用我会在文末介绍。
[Route("api/[controller]")] public class CustomersController : Controller { private readonly IConfiguration configuration; private readonly string connectionString; public CustomersController(IConfiguration configuration) { this.configuration = configuration; this.connectionString = configuration["mssql:connectionString"]; } // 获取指定ID的客户信息 [HttpGet("{id}")] public async Task<IActionResult> Get(Guid id) { const string sql = "SELECT [CustomerId] AS Id, [CustomerName] AS Name FROM [dbo].[Customers] WHERE [CustomerId]=@id"; using (var connection = new SqlConnection(connectionString)) { var customer = await connection.QueryFirstOrDefaultAsync<Model.Customer>(sql, new { id }); if (customer == null) { return NotFound(); } return Ok(customer); } } // 创建新的客户信息 [HttpPost] public async Task<IActionResult> Create([FromBody] dynamic model) { var name = (string)model.Name; if (string.IsNullOrEmpty(name)) { return BadRequest(); } const string sql = "INSERT INTO [dbo].[Customers] ([CustomerId], [CustomerName]) VALUES (@Id, @Name)"; using (var connection = new SqlConnection(connectionString)) { var customer = new Model.Customer(name); await connection.ExecuteAsync(sql, customer); return Created(Url.Action("Get", new { id = customer.Id }), customer.Id); } } }代码一如既往的简单,Web API控制器通过Dapper简单地实现了客户信息的创建和返回。我们不妨测试一下,使用下面的Invoke-RestMethod PowerShell指令,发送Post请求,通过上面的Create方法创建一个用户:
可以看到,response中已经返回了新建客户的ID号。接下来,继续使用Invoke-RestMethod来获取新建客户的详细信息:
OK,API调试完全没有问题。下面,我们将这个案例再扩充一下,我们希望这个API在完成客户信息创建的同时,向外界发送一条“客户信息已创建”的事件,并设置一个事件处理器,负责将该事件的详细内容保存到数据库中。
加入事件总线和消息处理机制首先,我们在ASP.NET Core Web API项目上,添加对以上两个程序集的引用,然后,按常规做法,在ConfigureServices方法中,将PassThroughEventBus添加到IoC容器中:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddSingleton<IEventBus, PassThroughEventBus>(); }