温莎城堡命令模式 [英] Castle Windsor & Command Pattern

查看:66
本文介绍了温莎城堡命令模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to implement a Command, CommandHandler and CommandDispatcher pattern using Castle Windsor without manually asking the container to resolve a CommandHandler based on Command type (which is generally considered an anti-pattern).

我找到了较早的文章,但是 ITypedFactoryComponentSelector 的实现已更改,因此现在它返回一个Func,而不是 TypedFactoryComponent

I found this old article, but the implementation of ITypedFactoryComponentSelector has changed, so now it returns a Func, instead of TypedFactoryComponent.

无论如何,如果有人可以对正确的答案有所了解,我将不胜感激。执行此模式。
当前设置(简化):

Anyway, I would really appreciate if someone can shed some light on the "correct" implementation of this pattern. Current setup (simplified):

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 并简单地执行类似

Basically all I want is to configure the container in a way that my WebAPI controller can have a dependency on ICommandDispatcher and simply do something like

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

谢谢! ;)

推荐答案

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

I finally managed to find a minimal fully functional solution by combining the Castle Windsor documentation and a few blog posts. I hope my answer will save someone a few hours.

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

For the base setup and missing code, please refer to my question above (I don't want to duplicate a lot of code).

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

Firstly we need to create an interface for our factory, but with no actual implementation (this is used by Castle Windsor to create a factory that will provide a particular implementation of your CommandHandler<T>:

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

然后添加以下CW安装程序代码:

Then add the following CW Installer code:

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)));
    }
}

所以魔术是在此行 Component.For< ICommandHandlerFactory>()。AsFactory()中,它告诉CW使用您的界面来创建要在<$ c $中使用的工厂c> CommandDispatcher :

so the magic is in this line Component.For<ICommandHandlerFactory>().AsFactory() as it tells CW to use your interface to create a factory that you will use in your 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;
        }

    }
}

巨大的GOTCHA

如果您将工厂方法命名为 GetCommandHandler ,CW将尝试解决一个名叫 CommandHandler 的类型,它将因为您没有这种类型而失败。根据文档 HERE CW应该回退到基于类型的非Get查找,但是它似乎没有这样做,只是吐出 ComponentNotFoundException 。因此,将工厂方法命名为 Get *

If you name your factory method something like GetCommandHandler, CW will try to resolve a type literally called CommandHandler and it will fail as you don't have such type. According to docs HERE CW should fall back to non-Get, type-based lookup, but it does not seem to do that and simply spits back a ComponentNotFoundException. So name your factory method ANYTHING but Get*

这篇关于温莎城堡命令模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆