使用SimpleInjector处理数据库连接错误的结构

时间:2012-09-22 18:13:56

标签: .net architecture dependency-injection simple-injector

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

  1. 使用Simple Injector DI框架初始化组合根和注册类型
  2. Init应用程序(使用Entity Framework对SQL数据库进行一些读写)
  3. 基于计时器,任务函数获取将运行ICommandHandler<CommandType>的命令的实例(命令类型不同)
  4. 在此实例上调用Handle(commandType)以运行命令
  5. 回到第3步
  6. 我需要保护程序在失去与SQL服务器的连接时崩溃。目前,如果应用程序丢失了SQL连接,则会抛出未处理的异常EntityException - The underlying provider failed on Open

    一旦服务器重新上线,程序应该能够重置并恢复操作。

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

    可以打开任何其他建议的体系结构,以及如何从更高级别捕获错误的位置查看,以便进行状态更改。

    以下代码结构

    我正在使用Command / Handler方法:

    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>,其中将返回一个实例化的BusinessUnitCommandHandlers对象,允许我Handle()命令。

    我见过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();
        }
    }
    

1 个答案:

答案 0 :(得分:1)

  

我需要保护程序在发生丢失时不会崩溃   连接到SQL服务器。

     

...应该使用的功能以一种很好的方式捕获错误   简单的注射器 - 特别是我专注于装饰器而不是   确定这是否正确。

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

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

我们甚至可以争辩说,甚至可能是这块基础结构代码的责任来进行崩溃保护(而不是命令处理程序的责任),特别是因为那段代码必须执行命令重新安排的事情。 (基于故障类型),或将其置于某种“失败的命令队列”(以允许手动重新运行它们)。也许您的设计可以从使用Circuit Breaker Pattern

中受益