温莎城堡和命令模式
Castle Windsor & Command Pattern
我正在尝试使用 Castle Windsor 实现 Command、CommandHandler 和 CommandDispatcher 模式,而无需手动要求容器根据 Command 类型(通常被认为是反模式)解析 CommandHandler。
我找到了 this 旧文章,但是 ITypedFactoryComponentSelector
的实现已经改变,所以现在 returns 一个 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();
}
谢谢! ;)
您所描述的是一个基于接口的类型化工厂,即一个根据您传递给它的参数解析组件的工厂,无需任何实现,也无需您手动解析组件。 Here is an answer that details how to use the typed factory mechanism, and here 是来自 kozmic.net 的一篇文章,对其进行了更详细的介绍。
通过结合 Castle Windsor 文档和几篇博文,我终于设法找到了一个最小的全功能解决方案。我希望我的回答能为别人节省几个小时。
基本设置和遗漏代码,请参考我上面的问题(我不想重复很多代码)。
首先我们需要为我们的工厂创建一个接口,但没有实际的实现(Castle Windsor 使用它来创建一个工厂,该工厂将提供您的 CommandHandler<T>
:
public interface ICommandHandlerFactory
{
ICommandHandler<TCommand> Resolve<TCommand>() where TCommand : ICommand;
}
然后添加以下 CW 安装程序代码:
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<ICommandHandlerFactory>().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*
以外的任何名称
我正在尝试使用 Castle Windsor 实现 Command、CommandHandler 和 CommandDispatcher 模式,而无需手动要求容器根据 Command 类型(通常被认为是反模式)解析 CommandHandler。
我找到了 this 旧文章,但是 ITypedFactoryComponentSelector
的实现已经改变,所以现在 returns 一个 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();
}
谢谢! ;)
您所描述的是一个基于接口的类型化工厂,即一个根据您传递给它的参数解析组件的工厂,无需任何实现,也无需您手动解析组件。 Here is an answer that details how to use the typed factory mechanism, and here 是来自 kozmic.net 的一篇文章,对其进行了更详细的介绍。
通过结合 Castle Windsor 文档和几篇博文,我终于设法找到了一个最小的全功能解决方案。我希望我的回答能为别人节省几个小时。
基本设置和遗漏代码,请参考我上面的问题(我不想重复很多代码)。
首先我们需要为我们的工厂创建一个接口,但没有实际的实现(Castle Windsor 使用它来创建一个工厂,该工厂将提供您的 CommandHandler<T>
:
public interface ICommandHandlerFactory
{
ICommandHandler<TCommand> Resolve<TCommand>() where TCommand : ICommand;
}
然后添加以下 CW 安装程序代码:
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<ICommandHandlerFactory>().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*