指定要在运行时从类型工厂解析的组件名称
Specify component name to resolve from type factory at runtime
假设我有一个接口:
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()
将其归结为 container.Resolve<IService>("XXX")
,我希望只使用 Get
作为方法名称让我在运行时指定它,但显然情况并非如此。
文档还说您可以在方法名称中使用 Create
一词,以便能够将构造函数参数转发给工厂创建的组件,但这也不是我想要的。
内置类型化工厂是否允许这种行为?我意识到我可以实现自定义 ITypedFactoryComponentSelector
,但如果我要走那么远,那么我也可以实现自己的具体工厂 class。
I realise I can implement a custom ITypedFactoryComponentSelector but if I'm going that far then I may as well just implement my own concrete factory class anyway.
如果您实现自己的 ITypedFactoryComponentSelector
,那么您正在创建逻辑,告诉工厂要 select 实现哪个。但是一旦做出该决定,selected 实现仍在从容器中解析。与仅实施您自己的具体工厂 class 相比,这是一个巨大的好处。如果实现是从容器中解析出来的,那么它们可以有自己的独立依赖关系,这对容器来说很容易管理,但如果具体工厂必须详细了解如何构建这些对象及其依赖关系等,则需要更多工作。
如果您尝试获取命名实现并且名称是运行时可用的字符串,那么这非常简单。
public class MyComponentSelector : DefaultTypedFactoryComponentSelector
{
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
return arguments[0].ToString();
}
}
它只是将您传入的字符串用作组件名称。无论您将什么名称传递给工厂的 Create
方法,这就是返回的组件名称。
如果您传入一些其他 class 或值,并且此 selector 根据输入确定组件名称,这将更有用。
这是一个更详细的示例,其中组件名称不是直接从传递给工厂的字符串 select 编辑的,而是由传入的对象确定的。这更实用一些,因为我们的应用程序代码不太可能知道我们的 DI 设置中的组件名称。
这是一个 selector。它需要一个 Address
对象,并使用从地址到 select 组件名称的国家/地区代码来实现 AddressValidator
。它还指定了回退的使用,以便在没有匹配项时可以使用 "generic" 地址验证器。
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())
);
}
}
这还允许您为不同的名称重复使用相同的组件。例如,假设法国和德国都使用相同的欧盟特定验证器——您可以在两个名称下注册相同的实现。
假设我有一个接口:
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()
将其归结为 container.Resolve<IService>("XXX")
,我希望只使用 Get
作为方法名称让我在运行时指定它,但显然情况并非如此。
文档还说您可以在方法名称中使用 Create
一词,以便能够将构造函数参数转发给工厂创建的组件,但这也不是我想要的。
内置类型化工厂是否允许这种行为?我意识到我可以实现自定义 ITypedFactoryComponentSelector
,但如果我要走那么远,那么我也可以实现自己的具体工厂 class。
I realise I can implement a custom ITypedFactoryComponentSelector but if I'm going that far then I may as well just implement my own concrete factory class anyway.
如果您实现自己的 ITypedFactoryComponentSelector
,那么您正在创建逻辑,告诉工厂要 select 实现哪个。但是一旦做出该决定,selected 实现仍在从容器中解析。与仅实施您自己的具体工厂 class 相比,这是一个巨大的好处。如果实现是从容器中解析出来的,那么它们可以有自己的独立依赖关系,这对容器来说很容易管理,但如果具体工厂必须详细了解如何构建这些对象及其依赖关系等,则需要更多工作。
如果您尝试获取命名实现并且名称是运行时可用的字符串,那么这非常简单。
public class MyComponentSelector : DefaultTypedFactoryComponentSelector
{
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
return arguments[0].ToString();
}
}
它只是将您传入的字符串用作组件名称。无论您将什么名称传递给工厂的 Create
方法,这就是返回的组件名称。
如果您传入一些其他 class 或值,并且此 selector 根据输入确定组件名称,这将更有用。
这是一个更详细的示例,其中组件名称不是直接从传递给工厂的字符串 select 编辑的,而是由传入的对象确定的。这更实用一些,因为我们的应用程序代码不太可能知道我们的 DI 设置中的组件名称。
这是一个 selector。它需要一个 Address
对象,并使用从地址到 select 组件名称的国家/地区代码来实现 AddressValidator
。它还指定了回退的使用,以便在没有匹配项时可以使用 "generic" 地址验证器。
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())
);
}
}
这还允许您为不同的名称重复使用相同的组件。例如,假设法国和德国都使用相同的欧盟特定验证器——您可以在两个名称下注册相同的实现。