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:
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); }
但说实话,我认为你想要做的事情似乎并不合适:
- 使您对内核的访问权限保持在最低限度。 你在这里做的是与Ninject 类似的ServiceLocator 。
- 如果没有可用于预期实例的绑定,我宁愿期望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时,将使用第一个绑定。 指定名称时,将使用指定的绑定。 这不是最漂亮的解决方案,但它确实有效。
- Windows Phone 8 HttpClient Get方法返回奇怪的结果
- 如果我从代码创建文本块,如何计算加载时的textbock高度和宽度?
- entity framework4.1的SQL“不在”语法
- 关键字“用户”附近的语法不正确
- 不支持Linq-to-EF DateTime.ToLocalTime
- VC ++在解决方案中从非/ clr项目的函数调用/ clr项目的函数
- Windows Phone 8 – 播放流式音频(网络电台)
- 在PresentationFramework.dll中发生了System.Windows.Markup.XamlParseException?
- 将XSD转换为SQL关系表