将事件数据保存到SQL Server数据库中

时间:2022-04-03 06:10:38

在上文中,我介绍了事件驱动型架构的一种简单的实现,并演示了一个完整的事件派发、订阅和措置惩罚惩罚的流程。这种实现太简单了,百十行代码就展示了一个根基事情道理。然而,要将这样的解决方案运用到实际出产环境,还有很长的路要走。今天,我们就研究一下在事件措置惩罚惩罚器中,东西生命周期的打点问题。

事实上,不只仅是在事件措置惩罚惩罚器中,我们需要关心东西的生命周期,在整个ASP.NET Core Web API的应用措施里,我们需要理解并仔细推敲被注册到IoC容器中的处事,它们的生命周期应该是个怎样的情形,这也是处事端应用措施设计必需当真考虑的内容。因为如果生命周期打点不同理,措施申请的资源无法合理释放,最后便会带来内存泄漏、措施瓦解等各类问题,然而这样的问题对付处事端应用措施来说,长短常严重的。

记得在上一篇文章的结束部分,我给大家留下一个操练,就是让大家在CustomerCreatedEventHandler事件措置惩罚惩罚器的HandleAsync要领中,填入本身的代码,以便对获得的事件动静做进一步的措置惩罚惩罚。作为本文的引子,我们首先将这部分事情做完,然后再进一步阐产生命周期的问题。

Event Store

Event Store是CQRS体系布局模式中最为重要的一个构成部分,它的主要职责就是生存产生于范围模型中的范围事件,并对事件数据进行归档。当仓储需要获取范围模型东西时,Event Store也会共同快照数据库一起,按照范围事件的产生挨次,逐步回放并重塑范围模型东西。事实上,Event Store的实现长短常庞大的,虽然从它的职责上来看并不算太庞大,然而它所需要解决的事件同步、快照、性能、动静派发等问题,使得CQRS体系布局的实现变得非常庞大。在实际应用中,已经有一些对照成熟的框架和工具集,能够辅佐我们在CQRS中很便利地实现Event Store,好比GetEventStore就是一个很好的开源Event Store框架,它是基于.NET开发的,在微软官方的eShopOnContainers说明文档中,也提到了这个框架,保举大家上他们的官网(https://eventstore.org/)了解一下。在这里我们就先不深入研究Event Store应该如何实现,我们先做一个简单的Event Store,以便展示我们需要讨论的问题。

延续着上一版的代码库(https://github.com/daxnet/edasample/tree/chapter_1),我们首先在EdaSample.Common.Events定名空间下,界说一个IEventStore的接口,这个接口非常简单,仅仅包罗一个生存事件的要领,代码如下:

public interface IEventStore : IDisposable { Task SaveEventAsync<TEvent>(TEvent @event) where TEvent : IEvent; }

SaveEventAsync要领仅有一个参数:由泛型类型TEvent绑定的@event东西。泛型约束暗示SaveEventAsync要领仅能接受IEvent接口及其实现类型的东西作为参数传入。接口界说好了,下一步就是实现这个接口,对传入的事件东西进行生存。为了实现过程的简单,我们使用Dapper,将事件数据生存到SQL Server数据库中,来模拟Event Store对事件的生存操纵。

Note:为什么IEventStore接口的SaveEventAsync要领签名中,没有CancellationToken参数?严格来说,撑持async/await异步编程模型的要领界说上,是需要带上CancellationToken参数的,以便挪用方请求打消操纵的时候,要领内部可以按照情况对操纵进行打消。然而有些情况下打消操纵并不是那么合理,或者要领内部所使用的API并没有供给更深层的打消撑持,因此也就没有须要在要领界说上增加CancellationToken参数。在此处,为了保证接口的简单,没有引入CancellationToken的参数。

接下来,我们实现这个接口,并用Dapper将事件数据生存到SQL Server中。出于框架设计的考虑,我们新建一个Net Standard Class Library项目,在这个新的项目中实现IEventStore接口,这么做的原因已经在上文中介绍过了。代码如下:

public class DapperEventStore : IEventStore { private readonly string connectionString; public DapperEventStore(string connectionString) { this.connectionString = connectionString; } public async Task SaveEventAsync<TEvent>(TEvent @event) where TEvent : IEvent { const string sql = @"INSERT INTO [dbo].[Events] ([EventId], [EventPayload], [EventTimestamp]) VALUES (@eventId, @eventPayload, @eventTimestamp)"; using (var connection = new SqlConnection(this.connectionString)) { await connection.ExecuteAsync(sql, new { eventId = @event.Id, eventPayload = JsonConvert.SerializeObject(@event), eventTimestamp = @event.Timestamp }); } } #region IDisposable Support // 此处省略 #endregion }

IDisposable接口的实现部分暂且省略,可以看到,实现还长短常简单的:通过结构函数传入数据库的连接字符串,在SaveEventAsyc要领中,基于SqlConnection东西执行Dapper的扩展要领来完成事件数据的生存。