【问题标题】:Castle Windsor & Command Pattern温莎城堡和指挥模式
【发布时间】:2015-06-19 16:40:16
【问题描述】:

我正在尝试使用 Castle Windsor 实现 Command、CommandHandler 和 CommandDispatcher 模式,而无需手动要求容器根据 Command 类型(通常被认为是反模式)解析 CommandHandler。

我找到了this的旧文章,但是ITypedFactoryComponentSelector的实现已经改变,所以现在它返回一个Func,而不是TypedFactoryComponent

无论如何,如果有人能阐明这种模式的“正确”实现,我将不胜感激。 当前设置(简化):

public interface ICommand {}
    
public class CreateUserCommand:ICommand
{
  public string Name { get;set; }
}

public interface ICommandHandler<in TCommand> where TCommand: ICommand
{
    ICommandResult Execute(TCommand command);
}

public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand>
{
    public ICommandResult Execute(CreateUserCommand command)
    {
        // some logic here
        return new CommandResult() {Success = true};
    }
}

public interface ICommandDispatcher
{
    ICommandResult Submit<TCommand>(TCommand command) where TCommand: ICommand;
}

public class CommandDispatcher : ICommandDispatcher
{
    // I DO NOT WANT TO DO THIS:
    IWindsorContainer _container;
    public CommandDispatcher(IWindsorContainer container)
    {
        _container = container;
    }

    public ICommandResult Submit<TCommand>(TCommand command) where TCommand : Commands.ICommand
    {
        // I DO NOT WANT TO DO THIS TOO:
        var handler = _container.Resolve<ICommandHandler<TCommand>>();
        if (handler == null)
        {
            throw new Exception("Command handler not found for command " + typeof(TCommand).ToString());
        }

        return handler.Execute(command);
    }
}

基本上我想要的只是配置容器,使我的 WebAPI 控制器可以依赖 ICommandDispatcher 并简单地执行类似的操作

var result = this.commandDispatcher.Submit(new CreateUserCommand("John Smith"));
if (result.Success){
  return Ok();
}

谢谢! ;)

【问题讨论】:

    标签: c# dependency-injection inversion-of-control castle-windsor


    【解决方案1】:

    通过结合 Castle Windsor 文档和一些博客文章,我终于找到了一个功能齐全的最小解决方案。我希望我的回答可以为某人节省几个小时。

    关于基本设置和缺少的代码,请参考我上面的问题(我不想重复很多代码)。

    首先我们需要为我们的工厂创建一个接口,但没有实际实现(Castle Windsor 使用它来创建一个工厂,它将提供您的CommandHandler&lt;T&gt; 的特定实现:

    public interface ICommandHandlerFactory
    {
        ICommandHandler<TCommand> Resolve<TCommand>() where TCommand : ICommand;
    }
    

    然后添加以下 CW Installer 代码:

    public class CommandingInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.AddFacility<TypedFactoryFacility>()
                .Register(
                    Classes.FromThisAssembly()
                        .BasedOn(typeof (ICommandHandler<>))
                        .WithServiceAllInterfaces()
                        .LifestyleTransient(),
                    Component.For<ICommandHandlerFactory>().AsFactory(),
                    Component.For<ICommandDispatcher>().ImplementedBy(typeof (CommandDispatcher)));
        }
    }
    

    所以 magic 在这一行 Component.For&lt;ICommandHandlerFactory&gt;().AsFactory() 中,因为它告诉 CW 使用您的界面来创建一个您将在您的 CommandDispatcher 中使用的工厂:

    public class CommandDispatcher : ICommandDispatcher
    {
        ICommandHandlerFactory _commandHandlerFactory;
        public CommandDispatcher(ICommandHandlerFactory commandHandlerFactory)
        {
            _commandHandlerFactory = commandHandlerFactory;
        }
    
        public ICommandResult Submit<TCommand>(TCommand command) where TCommand : Commands.ICommand
        {
            try
            {
                var handler = _commandHandlerFactory.Resolve<TCommand>();
                return handler.Execute(command);
            }
            catch (ComponentNotFoundException cnfex)
            {
                // log here
                throw cnfex;
            }
    
        }
    }
    

    巨大的陷阱

    如果您将工厂方法命名为 GetCommandHandler 之类的名称,CW 将尝试解析字面上称为 CommandHandler 的类型,但由于您没有此类类型,因此它将失败。根据文档HERE,CW 应该回退到非 Get、基于类型的查找,但它似乎并没有这样做,只是简单地返回一个 ComponentNotFoundException。因此,将您的工厂方法命名为 Get*

    【讨论】:

      【解决方案2】:

      您所描述的是基于接口的类型化工厂,即根据您传递的参数解析组件的工厂,无需任何实现,也无需手动解析组件。 Here 是一个回答,详细说明了如何使用类型化工厂机制,here 是来自 kozmic.net 的一篇文章,详细说明了这一点。

      【讨论】:

      • 谢谢 samy,我已经找到了那些文章,但不幸的是它仍然无法正常工作。经过几个小时的斗争,我终于设法让它工作,我将在下面发布一个完整的答案。无论如何感谢您的帮助!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多