我有一个具有几个不同具体实现的接口.我试图给Ninject一个默认使用,如果名称匹配,只使用其他实现.例如,我有以下绑定.
Bind<ISomething>().To<DefaultSomething>()
Bind<ISomething>().To<OtherSomething>().Named("55abd8b8-097f-4e1c-8d32-95cc97910604");
Run Code Online (Sandbox Code Playgroud)
我想要的是,如果命名部分不匹配,使用DefaultSomething实现.当我传入明确绑定的guid时,它工作正常.当我传入任何其他guid时,我得到"没有匹配的绑定可用"异常.
Bind<ISomething>().To<OtherSomething>().Named("55abd8b8-097f-4e1c-8d32-95cc97910604");
Bind<ISomething>().To<DefaultSomething>()
Bind<ISomething>().To<DefaultSomething>()
Bind<ISomething>().To<OtherSomething>().When(ctx => ctx.Service != null && ctx.Service.Name == "55abd8b8-097f-4e1c-8d32-95cc97910604");
Run Code Online (Sandbox Code Playgroud)
我也尝试过使用.当检查绑定时,我尝试颠倒了下面的顺序但是我永远无法绑定,除非我传入明确命名的Guid.
这篇文章似乎表明默认绑定有效,所以我一定做错了.有什么建议?
编辑:这是一个完整的例子,显示我想解决的问题.期望的行为是kernel.Get<INumber>("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<INumber>().To<UnknownNumber>();
kernel.Bind<INumber>().To<One>().Named("One");
kernel.Bind<INumber>().To<Two>().Named("Two");
Console.WriteLine(kernel.Get<INumber>("One").Write());
Console.WriteLine(kernel.Get<INumber>("Two").Write());
Console.WriteLine(kernel.Get<INumber>("Three").Write());
Console.ReadLine();
}
}
}
Run Code Online (Sandbox Code Playgroud)
Rem*_*oor 22
你完全错过了命名绑定:
赋予绑定名称不是条件.在没有约束的情况下请求它们时,您仍将获得所有这些.添加名称本身无变化.
使用名称请求实例会添加约束:
只返回名称与给定名称匹配的绑定
在您的情况下,您给了我一个绑定名称为的实例"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<ISomething>().To<Default>();
kernel.Bind<ISomething>().To<Other>()
.When(r => r.Parameters.OfType<CustomerIdParameter>()
.Single().Id == "SomeName");
kernel.Get<IWeapon>(new CustomerIdParameter("SomeName")).ShouldBeInstanceOf<Sword>();
Run Code Online (Sandbox Code Playgroud)
我留给你编写扩展方法,使定义和解决更容易.
选项2:
Bind<ISomething>().To<Default>().Binding.IsImplicit = true;
Bind<ISomething>().To<Other>().Named("SomeName")
public static T GetNamedOrDefault<T>(this IKernel kernel, string name)
{
return kernel.Get<T>(m => m.Name == null || m.Name == name);
}
Run Code Online (Sandbox Code Playgroud)
但老实说,我认为你想做的事似乎不是一个合适的设计:
在Ninject中完成此操作是非常可能的,它不会像默认情况下的分辨率行为那样.该IKernel.Get<T>扩展不问"默认"的结合,它要求任何约束力; 换句话说,它不适用任何约束.如果有多个匹配的绑定,则会抛出该效果的异常.
试试这两种扩展方法:
static class KernelExtensions
{
public static T GetDefault<T>(this IKernel kernel)
{
return kernel.Get<T>(m => m.Name == null);
}
public static T GetNamedOrDefault<T>(this IKernel kernel, string name)
{
T namedResult = kernel.TryGet<T>(name);
if (namedResult != null)
return namedResult;
return kernel.GetDefault<T>();
}
}
Run Code Online (Sandbox Code Playgroud)
第一个获得"默认"绑定 - 即您绑定的没有名称的绑定.第二个尝试获取命名绑定,但如果找不到,则恢复为默认值.
当然,雷莫也没错; 你应该避免以这种方式使用Ninject或任何其他容器,除非你有特别好的理由.这是服务定位器(反)模式,而不是真正的依赖注入.您应该使用When条件绑定的语法,使用复杂条件或仅修饰需要特殊绑定的类,即:
Bind<IFoo>().To<SpecialFoo>().WhenInjectedInto<ClassThatNeedsSpecialFoo>();
Run Code Online (Sandbox Code Playgroud)
要么...
Bind<IFoo>().To<SpecialFoo>().WhenMemberHas<SpecialAttribute>();
class InjectedClass
{
public InjectedClass([Special]IFoo) { ... }
}
Run Code Online (Sandbox Code Playgroud)
这是处理默认和条件绑定的正确方法.命名绑定实际上只在您尝试实现工厂模式并且希望将IoC容器包装在自定义工厂中时才有用.没关系,但是请谨慎使用它,因为你最终会以这种方式抛弃依赖注入的许多/大部分好处.
或者,您实际上可以实现自己的激活行为并使用它来覆盖Ninject中的默认行为 - 一切都是模块化的,并且被推入"组件"集合中.但这不适合胆小的人,所以我不打算在这里包含详细的教程.
您还可以简单地为您的绑定添加一个条件,使其没有条件,如下所示:
kernel.Bind<IObject>().To<Object1>().When(
x => x.ParentContext != null && !x.ParentContext.Binding.IsConditional)
.InRequestScope();
kernel.Bind<IObject>().To<Object2>().InRequestScope()
.Named("WCFSession");
Run Code Online (Sandbox Code Playgroud)
在没有指定名称的情况下执行标准注入时,将使用第一个绑定。指定名称时,将使用命名绑定。这不是最漂亮的解决方案,但它有效。