注入任何条件时绑定
Binding with when injected into any condition
我有一个服务接口
interface IService { }
和一些实现
class ServiceA : IService { }
class ServiceB : IService { }
class ServiceDefault : IService { }
而且我有 class 消耗 IService
,比如
class ServiceUse
{
public ServiceUse(IService svc) { }
}
应根据上下文状态将 IService
实现中的一个注入到消费 class 中。
为了实现这个,我有 ServiceProvider
class
static class ServiceNames
{
public const string ServiceA = "ServiceA";
public const string ServiceB = "ServiceB";
public const string ServiceDefault = "ServiceDefault";
}
class ServiceProvider : Provider<IService>
{
protected override IService CreateInstance(IContext context)
{
ServiceKindEnum kind = GetServiceKind(HttpContext.Current);
string bindingName = $"Service{kind}";
if (context.Kernel.CanResolve<IService>(bindingName))
return context.Kernel.Get<IService>(bindingName);
return context.Kernel.Get<IService>(ServiceNames.ServiceDefault);
}
...
}
绑定是
Bind<IService>().To<ServiceA>()
.Named(ServiceNames.ServiceA);
Bind<IService>().To<ServiceB>()
.Named(ServiceNames.ServiceB);
Bind<IService>().To<ServiceDefault>()
.Named(ServiceNames.ServiceDefault);
Bind<IService>().ToProvider<ServiceProvider>()
.WhenInjectedInto(typeof(ServiceUse));
每当另一个 class 消耗 IService
出现时,例如
class AnotherServiceUse
{
public AnotherServiceUse(IService svc) { }
}
我必须将其类型添加为指向 ServiceProvider
的绑定的 WhenInjectedInto
条件的参数
Bind<IService>().ToProvider<ServiceProvider>()
.WhenInjectedInto(typeof(ServiceUse), typeof(AnotherServiceUse));
因此,WhenInjectedInto
的参数列表在开发过程中可能会非常不稳定,有时这并不方便。
我正在寻找一种方法来减少对 WhenInjectedInto
参数列表的关注。我觉得我需要 WhenInjectedIntoAny
(或简单地 WhenInjected
)
Bind<IService>().ToProvider<ServiceProvider>()
.WhenInjected(); //target is not matter
有哪些选项?
正如你当前问题中的代码现在看起来,你有两个用例来解决 IService
:
- 您正在请求命名绑定 - 有多种服务,包括命名 "default service"。
- 你正在请求一个未命名的绑定,它总是通过相同的绑定(与提供者的绑定)解析 - 另外受到
WhenInjectedInto
. 的约束
如果您的示例实际上是完整的,则整个 WhenInjectedInto
可以通过声明不应对请求施加约束的条件来大大简化。因此,您的绑定应如下所示:
Bind<IService>().To<ServiceA>()
.Named(ServiceNames.ServiceA);
Bind<IService>().To<ServiceB>()
.Named(ServiceNames.ServiceB);
Bind<IService>().To<ServiceDefault>()
.Named(ServiceNames.ServiceDefault);
Bind<IService>().ToProvider<ServiceProvider>()
.When(request => request.Constraint == null);
在您的具体示例中,您还可以通过以下方式将 "preference" 赋予 ToProvider<ServiceProvider>()
绑定:
Bind<IService>().ToProvider<ServiceProvider>()
.When(request => true);
如果有多个可供选择,这将使 Ninject 选择此实现。提示:如果您有其他绑定条件 (When...
) 评估为真,这将不起作用。要将其移动到扩展方法中,请添加如下代码:
public static class NinjectExtensions
{
public static IBindingInNamedWithOrOnSyntax<T> MakePreferredBinding<T>(
this IBindingWhenSyntax<T> syntax)
{
return syntax.When(req => true);
}
}
如果这对您的方案不起作用,因为请求具有其他约束而不仅仅是名称,那么您可以采用基于参数的解决方案。对于有兴趣的读者,简而言之它是这样工作的:
- 创建一个自定义
IParameter
实现并保留一个名称。
- 基于
When(...)
创建自定义扩展绑定方法。这应该检查自定义参数是否在上下文中以及字符串是否匹配。将此应用到所有 "named" 服务绑定而不是 Named(...)
.
- 调整提供者以将自定义参数(带有名称)添加到请求中,而不是向 ninject 提供 "name" 以供通过
.Named(...)
进行选择。
我有一个服务接口
interface IService { }
和一些实现
class ServiceA : IService { }
class ServiceB : IService { }
class ServiceDefault : IService { }
而且我有 class 消耗 IService
,比如
class ServiceUse
{
public ServiceUse(IService svc) { }
}
应根据上下文状态将 IService
实现中的一个注入到消费 class 中。
为了实现这个,我有 ServiceProvider
class
static class ServiceNames
{
public const string ServiceA = "ServiceA";
public const string ServiceB = "ServiceB";
public const string ServiceDefault = "ServiceDefault";
}
class ServiceProvider : Provider<IService>
{
protected override IService CreateInstance(IContext context)
{
ServiceKindEnum kind = GetServiceKind(HttpContext.Current);
string bindingName = $"Service{kind}";
if (context.Kernel.CanResolve<IService>(bindingName))
return context.Kernel.Get<IService>(bindingName);
return context.Kernel.Get<IService>(ServiceNames.ServiceDefault);
}
...
}
绑定是
Bind<IService>().To<ServiceA>()
.Named(ServiceNames.ServiceA);
Bind<IService>().To<ServiceB>()
.Named(ServiceNames.ServiceB);
Bind<IService>().To<ServiceDefault>()
.Named(ServiceNames.ServiceDefault);
Bind<IService>().ToProvider<ServiceProvider>()
.WhenInjectedInto(typeof(ServiceUse));
每当另一个 class 消耗 IService
出现时,例如
class AnotherServiceUse
{
public AnotherServiceUse(IService svc) { }
}
我必须将其类型添加为指向 ServiceProvider
WhenInjectedInto
条件的参数
Bind<IService>().ToProvider<ServiceProvider>()
.WhenInjectedInto(typeof(ServiceUse), typeof(AnotherServiceUse));
因此,WhenInjectedInto
的参数列表在开发过程中可能会非常不稳定,有时这并不方便。
我正在寻找一种方法来减少对 WhenInjectedInto
参数列表的关注。我觉得我需要 WhenInjectedIntoAny
(或简单地 WhenInjected
)
Bind<IService>().ToProvider<ServiceProvider>()
.WhenInjected(); //target is not matter
有哪些选项?
正如你当前问题中的代码现在看起来,你有两个用例来解决 IService
:
- 您正在请求命名绑定 - 有多种服务,包括命名 "default service"。
- 你正在请求一个未命名的绑定,它总是通过相同的绑定(与提供者的绑定)解析 - 另外受到
WhenInjectedInto
. 的约束
如果您的示例实际上是完整的,则整个 WhenInjectedInto
可以通过声明不应对请求施加约束的条件来大大简化。因此,您的绑定应如下所示:
Bind<IService>().To<ServiceA>()
.Named(ServiceNames.ServiceA);
Bind<IService>().To<ServiceB>()
.Named(ServiceNames.ServiceB);
Bind<IService>().To<ServiceDefault>()
.Named(ServiceNames.ServiceDefault);
Bind<IService>().ToProvider<ServiceProvider>()
.When(request => request.Constraint == null);
在您的具体示例中,您还可以通过以下方式将 "preference" 赋予 ToProvider<ServiceProvider>()
绑定:
Bind<IService>().ToProvider<ServiceProvider>()
.When(request => true);
如果有多个可供选择,这将使 Ninject 选择此实现。提示:如果您有其他绑定条件 (When...
) 评估为真,这将不起作用。要将其移动到扩展方法中,请添加如下代码:
public static class NinjectExtensions
{
public static IBindingInNamedWithOrOnSyntax<T> MakePreferredBinding<T>(
this IBindingWhenSyntax<T> syntax)
{
return syntax.When(req => true);
}
}
如果这对您的方案不起作用,因为请求具有其他约束而不仅仅是名称,那么您可以采用基于参数的解决方案。对于有兴趣的读者,简而言之它是这样工作的:
- 创建一个自定义
IParameter
实现并保留一个名称。 - 基于
When(...)
创建自定义扩展绑定方法。这应该检查自定义参数是否在上下文中以及字符串是否匹配。将此应用到所有 "named" 服务绑定而不是Named(...)
. - 调整提供者以将自定义参数(带有名称)添加到请求中,而不是向 ninject 提供 "name" 以供通过
.Named(...)
进行选择。