假设我有一个界面:
public interface IService
{
void DoThing();
}
现在让我们说我想要一些这样的实现,所以我可以换掉我想在运行时使用的实现:
container.Register(Component.For<IService>().ImplementedBy<IServiceImplementationOne>().Named("First"));
container.Register(Component.For<IService>().ImplementedBy<IServiceImplementationTwo>().Named("Second"));
container.Register(Component.For<IService>().ImplementedBy<IServiceImplementationThree>().Named("Third"));
所以我可以通过名字选择一个:
container.Resolve<IService>("First"); //returns IServiceImplementationOne
container.Resolve<IService>("Second"); //returns IServiceImplementationTwo
container.Resolve<IService>("Third"); //returns IServiceImplementationThree
现在我希望能够使用内置类型工具在运行时选择我想要的那个,如果Windsor可以自动执行此操作,那么代码库中没有更多代码。
所以,让我们说我现在有工厂服务:
public interface IServiceFactory
{
IService Get(string name);
}
如果我这样注册:
container.Register(Component.For<IServiceFactory>().AsFactory());
如果我尝试拨打Get("First")
,我会收到一个例外情况,说它无法找到名为''
的注册。
根据文档,您的方法名称中Get
之后的任何内容都将用作要解析的名称,因此您可以在工厂界面中使用GetXXX()
将其归结为{{1}我希望只使用container.Resolve<IService>("XXX")
作为方法名称让我在运行时指定它,但显然情况并非如此。
文档还说你可以在方法名称中使用单词Get
来将构造函数参数转发给工厂创建的组件,但这不是我想要的。
内置类型工厂是否允许此行为?我意识到我可以实现一个自定义Create
,但如果我走得那么远,那么我也可以实现我自己的具体工厂类。
答案 0 :(得分:3)
我意识到我可以实现一个自定义的ITypedFactoryComponentSelector,但如果我走得那么远,那么我也可以实现我自己的具体工厂类。
如果您实施自己的ITypedFactoryComponentSelector
,那么您将创建逻辑,告知工厂选择哪个实施。但是一旦做出决定,所选的实现仍然是从容器中解决的。这比仅仅实施自己的混凝土工厂类更有利。如果实现是从容器中解析出来的,那么它们可以有自己的独立依赖关系,这对容器来说很容易管理,但如果具体工厂必须详细了解如何构造这些对象及其依赖关系等等,那就更多了。< / p>
如果您正在尝试获取命名实现,并且名称是运行时可用的字符串,那么这非常简单。
public class MyComponentSelector : DefaultTypedFactoryComponentSelector
{
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
return arguments[0].ToString();
}
}
它只是获取您传入的字符串并将其用作组件名称。无论您传递给工厂的Create
方法的名称是什么,这都是返回的组件名称。
如果你传入一些其他的类或值,这个更有用,这个选择器可以根据输入确定组件名称。
这里是一个更详细的例子,其中组件名称不是直接从传递给工厂的字符串中选择的,而是由传入的对象决定的。自从我们的应用程序以来,它更加实用一些代码不太可能知道DI设置中的组件名称。
这是一个选择器。它需要一个Address
对象,并使用该地址中的国家/地区代码来选择AddressValidator
实现的组件名称。它还指定使用后备,以便&#34;泛型&#34;如果没有匹配,可以使用地址验证器。
public class AddressValidatorSelector : DefaultTypedFactoryComponentSelector
{
public AddressValidatorSelector()
: base(fallbackToResolveByTypeIfNameNotFound: true) { }
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
return "AddressValidatorFor_" + ((Address)arguments[0]).CountryCode;
}
}
然后是实际的组件注册。它记录了几个特定的实现,后备和工厂本身。当它注册工厂时,它指定它将使用AddresssValidatorSelector
。
public class WindsorInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<TypedFactoryFacility>();
container.Register(
Component.For<IAddressValidator,UnitedStatesAddressValidator>()
.Named("AddressValidatorFor_USA"),
Component.For<IAddressValidator, FinlandAddressValidator>()
.Named("AddressValidatorFor_FIN"),
Component.For<IAddressValidator, MalawiAddressValidator>()
.Named("AddressValidatorFor_MWI"),
Component.For<IAddressValidator, CommonCountryAddressValidator>()
.Named("FallbackCountryAddressValidator")
.IsDefault()
);
container.Register(
Component.For<IAddressValidatorFactory>()
.AsFactory(new AddressValidatorSelector())
);
}
}
这也允许您为不同的名称重复使用相同的组件。例如,假设法国和德国都使用相同的欧盟特定验证器 - 您可以在两个名称下注册相同的实现。