Ninject默认的上下文绑定

我有一个具有几个不同具体实现的接口。 我试图给Ninject一个默认使用,如果名称匹配,只使用其他实现。 例如,我有以下绑定。

Bind().To() Bind().To().Named("55abd8b8-097f-4e1c-8d32-95cc97910604"); 

我想要的是,如果命名部分不匹配,使用DefaultSomething实现。 当我传入明确绑定的guid时,它工作正常。 当我传入任何其他guid时,我得到“没有匹配的绑定可用”exception。

 Bind().To().Named("55abd8b8-097f-4e1c-8d32-95cc97910604"); Bind().To() Bind().To() Bind().To().When(ctx => ctx.Service != null && ctx.Service.Name == "55abd8b8-097f-4e1c-8d32-95cc97910604"); 

我也试过使用。当检查绑定时,我试图像下面那样颠倒顺序但是我永远无法绑定,除非我传入明确命名的Guid。

这篇文章似乎表明默认绑定有效,所以我一定做错了。 有什么建议?


编辑:这是一个完整的例子,显示我试图解决的问题。 所需的行为是针对kernel.Get("Three").Write()返回"Unknown Number"

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using Ninject; namespace NinjectTest { interface INumber { string Write(); } class UnknownNumber : INumber { public string Write() { return "Unknown Number"; } } class One : INumber { public string Write() { return "1 = One"; } } class Two : INumber { public string Write() { return "2 = Two"; } } class Program { static void Main(string[] args) { StandardKernel kernel = new StandardKernel(); kernel.Bind().To(); kernel.Bind().To().Named("One"); kernel.Bind().To().Named("Two"); Console.WriteLine(kernel.Get("One").Write()); Console.WriteLine(kernel.Get("Two").Write()); Console.WriteLine(kernel.Get("Three").Write()); Console.ReadLine(); } } } 

你完全错过了命名绑定:

赋予绑定名称不是条件。 在没有约束的情况下请求它们时,您仍将获得所有这些。 添加名称本身无变化。

使用名称请求实例会添加约束:

只返回名称与给定名称匹配的绑定

在你的情况下,你给了我一个绑定名称为"three"的实例。 并且你希望它返回UnknownNumber ,它甚至没有名字。

这可以通过任何一个来实现

  1. 传递参数并向绑定添加条件以检查参数是否匹配, 或者
  2. 传递一个符合名称或未命名实例的约束,并声明隐含的未命名实例。

选项1:

 public class CustomerIdParameter : Parameter { public CustomerIdParameter(string id) : base("CustomerId", (object)null, false) { this.Id = id; } public string Id { get; private set; } } kernel.Bind().To(); kernel.Bind().To() .When(r => r.Parameters.OfType() .Single().Id == "SomeName"); kernel.Get(new CustomerIdParameter("SomeName")).ShouldBeInstanceOf(); 

我把它留给你来编写扩展方法,使定义和解决更容易。

选项2:

 Bind().To().Binding.IsImplicit = true; Bind().To().Named("SomeName") public static T GetNamedOrDefault(this IKernel kernel, string name) { return kernel.Get(m => m.Name == null || m.Name == name); } 

但说实话,我认为你想要做的事情似乎并不合适:

  1. 使您对内核的访问权限保持在最低限度。 你在这里做的是与Ninject 类似ServiceLocator
  2. 如果没有可用于预期实例的绑定,我宁愿期望exception而不是使用默认实例,因为这是一个错误。

在Ninject中完成此操作很有可能,它不会像默认情况下的分辨率行为那样。 IKernel.Get扩展名不要求“默认”绑定,它要求任何绑定; 换句话说,它不适用任何约束。 如果有多个匹配的绑定,则会抛出该效果的exception。

试试这两种扩展方法:

 static class KernelExtensions { public static T GetDefault(this IKernel kernel) { return kernel.Get(m => m.Name == null); } public static T GetNamedOrDefault(this IKernel kernel, string name) { T namedResult = kernel.TryGet(name); if (namedResult != null) return namedResult; return kernel.GetDefault(); } } 

第一个获得“默认”绑定 – 即你绑定的没有名字的绑定。 第二个尝试获取命名绑定,但如果找不到,则恢复为默认值。


当然,雷莫也没错; 你应该避免以这种方式使用Ninject或任何其他容器,除非你有特别好的理由。 这是服务定位器(反)模式,而不是真正的dependency injection。 您应该使用条件绑定的When语法,使用复杂条件或仅修饰需要特殊绑定的类,即:

 Bind().To().WhenInjectedInto(); 

要么…

 Bind().To().WhenMemberHas(); class InjectedClass { public InjectedClass([Special]IFoo) { ... } } 

这是处理默认和条件绑定的正确方法。 命名绑定实际上只在您尝试实现工厂模式并且希望将IoC容器包装在自定义工厂中时才有用。 没关系,但是请谨慎使用它,因为你最终会以这种方式抛弃dependency injection的许多/大部分好处。


或者,您实际上可以实现自己的激活行为并使用它来覆盖Ninject中的默认行为 – 一切都是模块化的,并且被推入“组件”集合中。 但这不适合胆小的人,所以我不打算在这里包含详细的教程。

您也可以简单地为绑定添加一个条件,使其没有条件,如下所示:

 kernel.Bind().To().When( x => x.ParentContext != null && !x.ParentContext.Binding.IsConditional) .InRequestScope(); kernel.Bind().To().InRequestScope() .Named("WCFSession"); 

在执行未指定Name的标准Inject时,将使用第一个绑定。 指定名称时,将使用指定的绑定。 这不是最漂亮的解决方案,但它确实有效。