使用Simple Injector

时间:2015-09-28 20:49:31

标签: c# dependency-injection simple-injector

使用Simple Injector,我尝试注册IEnumerable非通用ICommandHandler。这将是装饰我的通用ICommandHandler的所有实例的对象的集合。基本代码如下。我的CommandService类型取决于IEnumerable的{​​{1}}。

如何在我的作文根目录中注册?

ICommandHandler

我的public interface ICommandHandler { Type CommandType { get; } void Handle(object command); } public interface ICommandHandler<in TCommand> where TCommand : ICommand { void Handle(TCommand command); } public class WeaklyTypedCommandHandler<TCommand> : ICommandHandler, ICommandHandler<TCommand> where TCommand : ICommand { private readonly ICommandHandler<TCommand> _commandHandler; public WeaklyTypedCommandHandler(ICommandHandler<TCommand> commandHandler) { _commandHandler = commandHandler; } public Type CommandType { get { return typeof (TCommand); } } public void Handle(object command) { _commandHandler.Handle((TCommand)command); } public void Handle(TCommand command) { _commandHandler.Handle(command); } }

CommandService

1 个答案:

答案 0 :(得分:3)

你应该认真考虑删除非泛型ICommandHandler接口,因为它存在的唯一原因是能够进行调度,但是有更好的方法可以做到这一点。你所做的只会使你的设计和注册变得非常复杂。

相反,请将CommandService重写为以下内容:

public class CommandService : ICommandService {
    private readonly Container _container; 
    private readonly ISerializationFormatter _formatter;

    public CommandService(Container container, ISerializationFormatter formatter) {
        _container = container;
        _formatter = formatter;
    }

    public void Execute(string command) {
        if (command == null) throw new ArgumentNullException("command");

        var request = _formatter.Deserialize<CommandRequest>(command);
        Type handlerType = typeof(ICommandHandler<>)
            .MakeGenericType(request.Command.GetType());           
        dynamic handler = _container.GetInstance(handlerType);
        handler.Handle((dynamic)(request.Command));
    }
}

您通常不希望让类具有依赖于DI库的类,但此类仅包含基础结构逻辑,因此您可以在Composition Root内移动它。由于Composition Root已经非常依赖你的DI库,it is fine将类作为组合根的一部分,也依赖于容器。

如果没有非通用ICommandHandler界面,您的注册将是儿童游戏:

container.Register(typeof(ICommandHandler<>), new[] { typeof(BusinessLayer).Assembly });

container.Register<ICommandService, CommandService>(Lifestyle.Singleton);

最后请注意,放弃in界面的ICommandHandler<in TCommand>关键字。命令及其处理程序具有一对一的映射关系;你将永远不会有一个派生命令及其使用相同命令处理程序实现的基本命令。使用in关键字仅向其他开发人员传达错误消息。是的我知道Resharper告诉你这样做,但你不应该盲目地按照你的工具。 Resharper知道你的设计很糟糕;-)。放弃in

但是如果你讨厌使用dynamic和反射,我建议使用以下模型:

public sealed class CommandService : ICommandService {
    private readonly Container _container; 
    private readonly ISerializationFormatter _formatter;

    public CommandService(Container container, ISerializationFormatter formatter) {
        _container = container;
        _formatter = formatter;
    }

    public void Execute(string command) {
        if (command == null) throw new ArgumentNullException("command");

        var request = _formatter.Deserialize<CommandRequest>(command);
        Type handlerType = typeof(WeaklyTypedCommandHandler<>)
            .MakeGenericType(request.Command.GetType());           
        var handler = (ICommandHandler)_container.GetInstance(handlerType);
        handler.Handle(request.Command);
    }

    private interface ICommandHandler {
        void Handle(object command);
    }

    public class WeaklyTypedCommandHandler<TCommand> : ICommandHandler {
        private readonly ICommandHandler<TCommand> _commandHandler;

        public WeaklyTypedCommandHandler(ICommandHandler<TCommand> commandHandler) {
            _commandHandler = commandHandler;
        }

        public void Handle(object command) {
            _commandHandler.Handle((TCommand)command);
        }
    }
}

这个模型混合了两个世界:

  • 使用容器获取WeaklyTypedCommandHandler
  • 此处理程序实现ICommandHandler(但不是ICommandHandler<T>),因此您可以执行两个简单的转换,而不需要反射。
  • WeaklyTypedCommandHandler和非通用ICommandHandler都是CommandService类的实施细节。
  • dynamic相比,即使您的命令处理程序实现(或装饰器)被(意外地)定义为内部,此模型仍将继续工作。
  • dynamic一样,但与使用反射API(使用Type.GetMethod().Invoke())相比,命令处理程序中抛出的异常不会包含在TargetInvocationException中。

此模型的缺点是您需要额外的类和接口,尽管额外代码的数量很少。