1

我有一个实体框架应用程序连接到一个单独的盒子上的 SQL 服务器。程序流程可以分为两种状态:

  1. 使用 Simple Injector DI 框架初始化组合根和注册类型
  2. 初始化应用程序(使用实体框架对 SQL 数据库进行一些读写操作)
  3. 基于计时器,任务函数获取即将运行的命令的实例ICommandHandler<CommandType>(命令类型不同)
  4. 调用Handle(commandType)此实例以运行命令
  5. 返回第 3 步

我需要保护程序在失去与 SQL 服务器的连接时崩溃。目前,如果应用程序失去 SQL 连接,将抛出未处理的异常EntityException - The underlying provider failed on Open

一旦服务器重新联机,该程序应该能够重置和恢复操作。

这个问题涉及到 Simple Injector 的使用,因为它是我的应用程序的核心,我对编写未初始化和运行状态之间的状态转换有一些想法,但想首先询问应该在哪里以一种很好的方式捕获错误使用Simple Injector 的功能 - 具体来说,我专注于装饰器,但不确定这是否正确。

对任何其他推荐架构开放,以及从更高级别捕获错误的位置来看这可能会如何,从而允许发生状态更改。

下面的代码结构

我正在使用命令/处理程序方法:

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

在应用程序启动时,所有实现的类型ICommandHandler<T>都被注册:

public static void Bootstrap(Container container)
{
    container.RegisterManyForOpenGeneric(
        typeof(ICommandHandler<>),
        System.AppDomain.CurrentDomain.GetAssemblies());

    // bootstrap container
}

我的命令处理程序实现如下:

public class AddBusinessUnitCommand
{
    public string Name { get; set; }
    public float TimeZone { get; set; }
}

public class BusinessUnitCommandHandlers
    : ICommandHandler<AddBusinessUnitCommand>
{
    private IBusinessUnitService businessUnitService;

    public BusinessUnitCommandHandlers(
        IBusinessUnitService businessUnitService)
    {
        this.businessUnitService = businessUnitService;
    }

    public void Handle(AddBusinessUnitCommand command)
    {
        // do something
    }
}

然后,我可以使用 Simple Injector 获取类型的实例,例如,将返回ICommandHandler<AddBusinessUnitCommand>一个实例化的对象,允许我执行命令。BusinessUnitCommandHandlersHandle()

我已经看到 Simple Injector 可以使用装饰器来包装Handle()过程调用。

public class TransactionCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private readonly ICommandHandler<TCommand> handlerToCall;
    private readonly IUnitOfWork unitOfWork;

    public TransactionCommandHandlerDecorator(
        IUnitOfWork unitOfWork, 
        ICommandHandler<TCommand> decorated)
    {
        this.handlerToCall = decorated;
        this.unitOfWork = unitOfWork;
    }

    public void Handle(TCommand command)
    {
         this.handlerToCall.Handle(command);
         unitOfWork.Save();
    }
}
4

1 回答 1

1

我需要保护程序在失去与 SQL 服务器的连接时崩溃。

... 应该使用 Simple Injector 的功能以一种很好的方式捕获错误 - 具体来说,我专注于装饰器,但不确定这是否正确。

ICommandHandler<T>使用泛型装饰器装饰实现有三个重要原因。首先,它可以防止您在应用程序中有任何重复的代码,因为装饰器是一次编写的,并且包装了许多ICommandHandler<T>实现。其次,您可以更改命令处理程序的行为并添加横切关注点,而无需对这些命令处理程序的使用者进行任何更改。最后,即使代码重用和消费者无知不是问题,如果要添加的行为在逻辑上是命令处理程序的一部分,装饰器仍然有用。例如验证执行的命令。这是消费者可能不应该关心的事情,并且是您在调用Handle.

我希望您的“命令处理器”(您的计时器)是一段代码,并且是您的应用程序基础架构的一部分(甚至可能是组合根的一部分)。由于这只是一段代码,因此没有理由使用装饰器来防止代码重复,并且添加这种崩溃保护作为装饰器可能也不会为您赢得那么多。

我们甚至可以争辩说,进行崩溃保护(而不是命令处理程序的责任)甚至可能是这段基础设施代码的责任,特别是因为那段代码可能必须执行命令重新调度之类的事情(基于失败类型),或将其放置在某种“失败的命令队列”中(以允许手动重新运行它们)。也许您的设计可以从使用断路器模式中受益。

于 2012-09-22T19:05:25.970 回答