注入任何条件时都会结合

时间:2017-07-20 15:07:51

标签: c# ninject

我有一个服务界面

interface IService { }

和许多实现

class ServiceA : IService { }
class ServiceB : IService { }
class ServiceDefault : IService { }

我的课程耗费IService,比如

class ServiceUse
{
    public ServiceUse(IService svc) { }
}

应根据上下文状态将其中一个IService实现注入到使用类中。

为实现这一目标,我有ServiceProvider

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));

每当出现消耗IService的其他类时,例如

class AnotherServiceUse
{
    public AnotherServiceUse(IService svc) { }
}

我必须添加其类型作为指向WhenInjectedInto的绑定的ServiceProvider条件的参数

Bind<IService>().ToProvider<ServiceProvider>()
    .WhenInjectedInto(typeof(ServiceUse), typeof(AnotherServiceUse));

因此,WhenInjectedInto的参数列表在开发过程中可能会非常不稳定,有时这不方便。

我正在寻找一种不太关注WhenInjectedInto参数列表的方法。我觉得我需要WhenInjectedIntoAny(或简称WhenInjected

之类的东西
Bind<IService>().ToProvider<ServiceProvider>()
    .WhenInjected(); //target is not matter

有哪些选择?

1 个答案:

答案 0 :(得分:1)

正如您当前问题中的代码现在所示,您有两个用于解析IService的用例:

  • 您正在请求命名绑定 - 有多项服务,包括命名的“默认服务”。
  • 您正在请求一个未命名的绑定,该绑定始终通过相同的绑定(与提供者绑定)进行解析 - 此外还受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);

在您的具体示例中,您还可以通过执行以下操作为ToProvider<ServiceProvider>()绑定提供“首选项”:

Bind<IService>().ToProvider<ServiceProvider>()
    .When(request => true);

这将使Ninject选择此实现,如果有多个可供选择。提示:如果您有其他条件(When...)评估为true,那么这将不起作用。要将其移动到扩展方法中,请添加如下代码:

public static class NinjectExtensions
{
    public static IBindingInNamedWithOrOnSyntax<T> MakePreferredBinding<T>(
        this IBindingWhenSyntax<T> syntax)
    {
        return syntax.When(req => true);
    }
}

如果这对您的方案不起作用,因为请求具有其他约束而不仅仅是名称,那么您可以使用基于参数的解决方案。对于感兴趣的读者,简而言之就是这样:

  • 创建一个名称的自定义IParameter实现。
  • 根据When(...)创建用于绑定的自定义扩展方法。这应检查自定义参数是否在上下文中以及字符串是否匹配。将此应用于所有“命名”服务绑定,而不是Named(...)
  • 调整提供程序,将自定义参数(带有名称)添加到为ninject提供“name”的请求intead中,以便通过.Named(...)进行选择。