Ninject 2.x细说---2.绑定和作用域
Ninject中提供多种接口和实现类的绑定的方式供我们选择,同时还可以设置相关的绑定项以及对象的作用域等。具体如下:(其中使用到的类和代码重用上一节的“Ninject 2.x细说---1.基本使用”中的定义)
1. 绑定:
Ninject中提供好几种绑定接口实现的方法,具体如下:
Ø To:绑定到接口的具体实现。
Ø ToConstant:绑定到某个常量值。
Ø ToMethod:绑定到方法。
Ø ToProvider:绑定到某个自定义的对象创建提供程序。
Ø ToSelf:自绑定。
代码如下:
1) 自绑定
Ninject可以使用ToSelf()方法,将自己绑定自己,这里必须是一个具体的类。
Bind<MessageCfg>().ToSelf();
2) 绑定到方法:
Bind<IMessage>().ToMethod(context => new MessageDB());
3) 绑定到具体的类型
Bind<IMessage>().ToConstant(new MessageDB());
4) 绑定到指定的服务提供程序
以及Bind<IMessage>().ToProvider(实现IProvider接口的服务提供程序);
2. 指定相关绑定信息:
Ninject中,可以在绑定的时候指定一些附加信息,以便更加明确构造函数或者是给某些属性赋值或者在绑定时回调方法。如下面的代码中:Bind<IMessage>().To<MessageDB>().WithConstructorArgument("msg", 1);
我们在绑定的同时指定了构造函数的参数以及值。
此外,还可以设置的指定信息分别有:
Ø WithConstructorArgument:指定构造函数中相关的参数,还有回调方法的重载。
Ø WithMetadata:指定相关元数据信息。
Ø WithParameter:指定相关的自定义参数。这个方法也实现构造函数参数值指定,与WithConstructorArgument类似,如:Bind<IMessage>().To<MessageDB>().WithConstructorArgument("msg", 1);同样可以写成:Bind<IMessage>().To<MessageDB>().WithParameter(new ConstructorArgument("msg", 1));
Ø WithPropertyValue:指定相关的属性值,还有回调方法的重载。
3. 条件绑定:
Ninject中还可以指定相关的绑定的条件,只有条件满足的情况的下,才将相关的接口实现绑定到相关的接口上。如:
Bind<IMessage>().To<MessageDB>().WhenInjectedInto<MessageCfg>();
上面的代码,由于MessageCfg依赖与IMessage接口,所以其意思是在MessageCfg类中依赖的IMessage接口与MessageDB类绑定。
类似的还有When()如:
Bind<IMessage>().To<MessageDB>().When(cxt => cxt.Service.Name.StartsWith("Msg"));
其他的条件还有WhenClassHas、WhenParentNamed、WhenTargetHas等条件绑定。
4. 设置注入项
在Ninject中可以通过NinjectSettings类来指定注入项。如:
////设置注入项
var settings = new NinjectSettings() { AllowNullInjection = true };
using (var kernal = new StandardKernel(settings, new MessageModule()))
{
var msgcfg = kernal.Get<MessageCfg>();
}
其中,可以设置的项有:
ActivationCacheDisabled、AllowNullInjection、CachePruningInterval、ExtensionSearchPattern、InjectAttribute、InjectNonPublic等等。大概对应的就是设置缓存是否启用、是否允许空注入、缓存周期、扩展查找位置、必须被注入的属性、是否必须注入非公开成员等等。(BTW:Ninject中摘要说明少了Get,还发现好几个地方都是这样呢)。
5. Inject特性
在Inject中,我们构造函数、属性和字段上加 [Inject]特性指示,以便指定注入的属性、方法和字段等。[Inject]特性定义具体如下:
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public class InjectAttribute : Attribute
{
public InjectAttribute();
}
例如使用[Inject]指定构造函数,如果某个类存在多个构造函数,那么我们就可以在某个构造函数上使用[Inject]来指定从此处注入,具体代码如下:
public class MessageDB : IMessage
{
public MessageDB() { }
public MessageDB(object msg)
{
Console.WriteLine("使用了object 参数构造:{0}", msg);
}
[Inject]
public MessageDB(int msg)
{
Console.WriteLine("使用了int 参数构造:{0}", msg);
}
public string GetMsgNumber()
{
return "从数据中读取消息号!";
}
}
相应的MessageModule进行修改,具体如下:
public class MessageModule : NinjectModule
{
public override void Load()
{
//绑定接口并指定接口的实现。
Bind<IMessage>().To<MessageDB>().WithConstructorArgument("msg", 1);
}
}
具体使用如下:
using (var kernal = new StandardKernel(new MessageModule()))
{
//属性注入
var msgcfg = new MessageCfg() { Msg = kernal.Get<IMessage>() };
Console.WriteLine(msgcfg.GetMsgNumber());
Console.Read();
}
其中MessageCfg类的定义见前一节介绍的内容。通过上面的代码,我们可以看到,MessageDB分别由int和object类型的构造函数,如果没有在构造函数上指定[Inject],经过测试发现它默认就是选择第一个构造函数,如果参数类型不匹配就直接抛出异常。
6. 对象作用域:
Transient |
.InTransientScope() |
每次调用创建新实例。 |
Singleton |
.InSingletonScope() |
单例,仅创建一个实例。 |
Thread |
.InThreadScope() |
每一个线程创建一个实例。 |
Request |
.InRequestScope() |
每当Web请求发起时创建一个实例,结束请求时释放实例 |
InScope |
InScope(Func) |
对象尽量保持到回调委托返回 |
上表来自“靠近太阳”的博文,后增加了InScope 。如使用InThreadScope()具体例子如下:
public class MessageModule : NinjectModule
{
public override void Load()
{
//绑定接口并指定接口的实现。
Bind<IMessage>().To<MessageDB>().InThreadScope().WithParameter(new ConstructorArgument("msg", 1));
}
}
使用代码如下:
using (var kernal = new StandardKernel(new MessageModule()))
{
//属性注入
var th1 = new Thread(new ThreadStart(() =>
{
var msgcfg = new MessageCfg() { Msg = kernal.Get<IMessage>() };
Console.WriteLine(msgcfg.GetMsgNumber());
}));
var th2 = new Thread(new ThreadStart(() =>
{
var msgcfg = new MessageCfg() { Msg = kernal.Get<IMessage>() };
Console.WriteLine(msgcfg.GetMsgNumber());
}));
var th3 = new Thread(new ThreadStart(() =>
{
var msgcfg = new MessageCfg() { Msg = kernal.Get<IMessage>() };
Console.WriteLine(msgcfg.GetMsgNumber());
}));
th1.Start();
th2.Start();
th3.Start();
Console.Read();
}
}
在上面的代码中,我们指定了对象在InThreadScope,在使用的代码中分别创建了3个线程来进行模拟,最终每个线程都是创建了一个对象。