我必须发送电子邮件、写入文件并调用 Web 服务。为了保持一致性,所有步骤都必须发生。如果任何步骤抛出异常或错误,则必须回滚所有步骤。
在我开始使用自己的对象 ACID 引擎之前,是否有任何普遍接受的模式用于在对象级别实现 ACID 语义?
更好的是,是否有任何现有的库可以用于 .NET 平台?
编辑:我知道发送电子邮件无法撤消,但无法连接到 SMTP 服务器是导致整个事务终止的原因。另外,我希望它可以扩展以用于未来的操作。
我必须发送电子邮件、写入文件并调用 Web 服务。为了保持一致性,所有步骤都必须发生。如果任何步骤抛出异常或错误,则必须回滚所有步骤。
在我开始使用自己的对象 ACID 引擎之前,是否有任何普遍接受的模式用于在对象级别实现 ACID 语义?
更好的是,是否有任何现有的库可以用于 .NET 平台?
编辑:我知道发送电子邮件无法撤消,但无法连接到 SMTP 服务器是导致整个事务终止的原因。另外,我希望它可以扩展以用于未来的操作。
我上一次看到这样的东西是几年前的事了。我记得的一点是它使用命令模式并将每个命令对象存储在队列中。我认为这是一个 LIFO 堆栈。
因此,如果“事务”失败,引擎会弹出一个命令对象,撤消命令,然后销毁命令对象。重复直到堆栈为空。如果“事务”成功,堆栈就会被清除。
不幸的是,我不记得更多了。
CSLA.NET实现了类似的撤消堆栈。这是我能想到的唯一代码示例。
当 ACID 语义可能不合适时,Windows Workflow Foundation 有一个补偿概念(使用复合活动)。当然,它也支持 ACID 事务。
一个很好的问题是为什么要为补偿而烦恼?一个具有自动回滚功能的大 ACID 事务不也一样好吗?当操作发生在同一数据库或同一信息系统内时,ACID 事务是最合适的。当操作快速结束时,它也是最合适的。当涉及不同的公司和服务时,根据 ACID 语义定义流程通常具有挑战性。为了使其隔离和持久,您必须在任务期间锁定不同公司的所有资源。这通常是不合理的,尤其是在任务很长的情况下。为了使其保持一致和原子,您需要临时补偿代码。
不严重依赖外部库的最简单的技术是流行。通过使用序列化定期检查点以获取您的状态快照,然后通过序列化针对您的数据的每个副作用操作的足够信息来维护日志,以便稍后重复。如果出现问题,请重新加载最近的检查点,然后重新应用在该点之后写入的所有日志记录。
对于更复杂的东西,试试软件事务内存。在当前语言中实现可能有些笨拙,但功能非常强大,并且还可以为您提供一些额外的并发技术。
对于访问 Web 服务或发送电子邮件等不可逆转的操作,您将需要使用补偿事务:进行另一个 Web 服务调用以取消或更新前一个的结果,或者可能发送另一封电子邮件通知收件人事情没有t 按预期工作。
由于您无法取消发送电子邮件,并且编写文件相对便宜,因此我只需按正确的顺序执行这些操作:
一种想法是使用 JMS 作为“引擎”,您可以利用 JMS 事务(可以加入现有事务,例如 DB 事务)。这开始导致异步事件驱动架构,这可能是一件好事,除非我们谈论的是简单的应用程序——在这种情况下,您可能不需要问这个问题。
这方面的一个例子是简单的帐户创建。为此,您希望将帐户信息保存到数据库,并向用户发送一封电子邮件以进行激活——但出于显而易见的原因,您希望他们在同一个事务中。
您不应该将电子邮件发送代码放在事务中,因为即使您可以发送电子邮件 - db 事务提交也可能由于某种原因而失败。您也不应该将电子邮件发送放在事务之外(在提交之后),因为电子邮件发送可能会失败,从而导致一个孤立帐户。
因此,要在这种情况下使用 JMS - 将 JMS 发送代码放在 DB 事务中并让它加入该事务。保证您的消息传递。在另一端有一些东西消耗队列发送电子邮件。如果电子邮件发送失败,最好的选择是记录/发出警报 - JMS 将回滚并将消息放回队列以供以后使用。即,一旦您希望解决任何问题,就尝试重新发送电子邮件。
关键是 - 数据库记录是一致的,并且最终会发送电子邮件。
两个想法:
此外,最近发布了一个实验项目STM .NET 。该项目将事务内存添加到 C#。它实际上修改了 CLR 以支持这一点。