注入任何条件时绑定

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(...) 进行选择。