很多其它类型的IOC容器过于依赖配置文件,老是配置,总感觉有点不爽,而且要使用assembly-qualified名称(也就是类型的全名)来进行定义,稍不注意就会因为打错字而令整个程序崩掉。Ninject是一个快如闪电、超轻量级的基于.Net平台的IOC容器,主要用来解决程序中模块的耦合问题,它的目的在于做到最少配置。因此如果你不喜欢配置,不喜欢重量级IOC框架,那么就用小苹果Ninject吧!
首先你必须获取Ninject,其官网下载地址:http://www.ninject.org,你也可以通过VS中的NuGet来加载Nniject,不论是哪种方式,最终的目的就是将 Ninject.dll 这个程序集引用到你的项目中。这样在你的项目中,如果想使用Ninject,只需添加其命名空间引用即可~
1.
using Ninject;
我们先定义一个记录日志的接口:
1.
public
interface
ILogger
2.
{
3.
void
Write(string message);
4.
}
然后用文件记录日志方式和数据库记录日志方式分别实现这个接口,不过这里只是演示而已,所以并没有真正去实现这两个类,你懂的~
01.
public
class
FileLogger : ILogger
02.
{
03.
public
void
Write(string message)
04.
{
05.
Console.WriteLine(String.Format(
'文件记录日志:{0}'
, message));
06.
}
07.
}
08.
09.
public
class
DBLogger : ILogger
10.
{
11.
public
void
Write(string message)
12.
{
13.
Console.WriteLine(String.Format(
'数据库记录日志:{0}'
, message));
14.
}
15.
}
在Ninject中,我们可以通过重写Ninject.Modules.NinjectModule类的方法Load()而实现依赖注入,注意这里用的是代码的形式!
1.
public
class
MyModule : Ninject.Modules.NinjectModule
2.
{
3.
public
override
void
Load()
4.
{
5.
Bind<ILogger>().To<FileLogger>();
6.
Bind<ILogger>().To<DBLogger>();
7.
}
8.
}
具体调用方法:
1.
private
static
IKernel kernel =
new
StandardKernel(
new
MyModule());
2.
static
void
Main(string[] args)
3.
{
4.
ILogger logger = kernel.Get<ILogger>();
//获取的是FileLogger
5.
logger.Write(
' Hello Ninject!'
);
6.
Console.Read();
7.
}
没错,用Ninject进行依赖注入就是这么爽歪歪~
这里我们使用构造函数注入一个栗子:
首先新建一个测试接口ITester与其实现类NinjectTester ,显然NinjectTester依赖于ILogger
01.
interface
ITester
02.
{
03.
void
Test();
04.
}
05.
06.
class
NinjectTester:ITester
07.
{
08.
public
string _Message{set;get;}
09.
10.
private
ILogger _logger;
11.
public
NinjectTester(ILogger logger)
12.
{
13.
_logger = logger;
14.
}
15.
public
void
Test()
16.
{
17.
_logger.Write(
'Hello Ninject!'
);
18.
}
19.
}
下面就看看用Ninject的方式实现依赖注入的过程中用到的一些东东~
其实就是接口IKernel的方法,把某个类绑定到某个接口,T1代表的就是接口或者抽象类,而T2代表的就是其实现类
例如:
1.
IKernel ninjectKernel =
new
StandardKernel();
2.
ninjectKernel.Bind<ILogger>().To<FileLogger>();
其实就是得到某个接口的实例,例如下面的栗子就是得到ILogger的实例FileLogger:
1.
ILogger myLogger= ninjectKernel.Get<ILogger>();
其实就是在绑定接口的实例时,同时给实例NinjectTester的属性赋值,例如:
1.
ninjectKernel.Bind<ITester>().To<NinjectTester>().WithPropertyValue(
'_Message'
,
'这是一个属性值注入'
);
其实就是说我们可以为实例的构造方法所用的参数赋值,例如:
01.
public
class
DefalutDiscountHelper : IDiscountHelper
02.
{
03.
private
decimal discountRate;
04.
public
decimal DiscountSize { get; set; }
05.
public
DefalutDiscountHelper(decimal discountParam)
06.
{
07.
discountRate = discountParam;
08.
}
09.
10.
public
decimal ApplyDiscount(decimal totalParam)
11.
{
12.
return
(totalParam - (discountRate / 100M * totalParam));
13.
}
14.
}
1.
ninjectKernel.Bind<IDiscountHelper>().To<DefalutDiscountHelper>().WithConstructorArgument(
'discountParam'
, 50M);
这个方法的意思是绑定到某个已经存在的常量,例如:
1.
StudentRepository sr =
new
StudentRepository();
2.
ninjectKernel.Bind<IStudentRepository>().ToConstant(sr);
这个方法意思是绑定到自身,但是这个绑定的对象只能是具体类,不能是抽象类。为什么要自身绑定呢?其实也就是为了能够利用Ninject解析对象本身而已。例如:
1.
ninjectKernel.Bind<StudentRepository>().ToSelf();
2.
StudentRepository sr = ninjectKernel.Get<StudentRepository>();
这个方法是条件绑定,就是说只有当注入的对象是某个对象的实例时才会将绑定的接口进行实例化
1.
ninjectKernel.Bind<IValueCalculater>().To<IterativeValueCalculatgor>().WhenInjectedInto<LimitShoppingCart>();
这个方法是为绑定的对象指明生命周期其实
1.
ninjectKernel.Bind<IStudentRepository>().To<StudentRepository>().InTransientScope();
2.
//每次调用创建新实例
3.
ninjectKernel.Bind<IStudentRepository>().To<StudentRepository>().InSingletonScope();
4.
//每次调用是同一个实例
这里的Load()方法其实是抽象类Ninject.Modules.NinjectModule的一个抽象方法,通过重写Load()方法可以对相关接口和类进行集中绑定,例如:
1.
public
class
MyModule : Ninject.Modules.NinjectModule
2.
{
3.
public
override
void
Load()
4.
{
5.
Bind<ILogger>().To<FileLogger>();
6.
Bind<ITester>().To<NinjectTester>();
7.
}
8.
}
这是通过Load()方法绑定之后的完整代码:
1.
private
static
IKernel kernel =
new
StandardKernel(
new
MyModule());
2.
static
void
Main(string[] args)
3.
{
4.
ITester tester = kernel.Get<ITester>();
// 因为是链式解析,因此只解析ITester即可,其它依赖的东东都会顺带解析
5.
tester.Test();
6.
Console.Read();
7.
}
在Inject中,我们可以通过在构造函数、属性和字段上加 Inject特性指定注入的属性、方法和字段等,例如下面的栗子,MessageDB有两个构造函数:int和object类型的。现在我们已经为int型的指定了Inject特性,因此在注入的时候选择的就是int型的构造函数;如果没有在构造函数上指定Inject特性,则默认选择第一个构造函数:
01.
public
class
MessageDB : IMessage
02.
{
03.
public
MessageDB() { }
04.
public
MessageDB(object msg)
05.
{
06.
Console.WriteLine(
'使用了object 参数构造:{0}'
, msg);
07.
}
08.
09.
[Inject]
10.
public
MessageDB(
int
msg)
11.
{
12.
Console.WriteLine(
'使用了int 参数构造:{0}'
, msg);
13.
}
14.
15.
public
string GetMsgNumber()
16.
{
17.
return
'从数据中读取消息号!'
;
18.
}
19.
}
答案是肯定的,要不怎么说Ninject是可扩展的呢,必须可以,但是如果这样的话,你就没必要用Ninject了,因为这样又回到了繁琐的配置上面,还不如用其他的IOC容器来得方便,这里用的是Ninject的XML扩展,你可以通过下面的地址https://github.com/ninject/ninject.extensions.xml下载扩展组件,主要也就是Ninject.Extensions.Xml这个东东起的作用,这里不想详说,给个栗子了结:
(1)配置文件
1.
<?xml version=
'1.0'
encoding=
'utf-8'
?>
2.
<module name=
'ServiceModule'
>
3.
<bind name=
'Txtlog'
service=
'LogService.ILogService,LogService'
to=
'LogService.Impl.TxtLogService,LogService'
/>
4.
<!--<bind name=
'Dblog'
service=
'LogService.ILogService,LogService'
to=
'LogService.Impl.DbLogService,LogService'
/>-->
5.
<bind name=
'Sword'
service=
'NinjectApp.Weapon.IWeapon,NinjectApp'
to=
'NinjectApp.Weapon.Sword,NinjectApp'
/>
6.
<bind name=
'FootSoldier'
service=
'NinjectApp.Warrior.IWarrior,NinjectApp'
to=
'NinjectApp.Warrior.FootSoldier,NinjectApp'
/>
7.
</module>
(2)利用扩展加载服务
01.
using System.Collections.Generic;
02.
using System.Xml.Linq;
03.
using Ninject;
04.
using Ninject.Extensions.Xml;
05.
using Ninject.Extensions.Xml.Handlers;
06.
07.
namespace NinjectApp
08.
{
09.
public
class
XmlModuleContext
10.
{
11.
protected
readonly IKernel kernel;
12.
protected
readonly IDictionary<string, IXmlElementHandler> elementHandlers;
13.
14.
public
XmlModuleContext()
15.
{
16.
kernel =
new
StandardKernel();
17.
elementHandlers =
new
Dictionary<string, IXmlElementHandler> { {
'bind'
,
new
BindElementHandler(kernel) } };
18.
}
19.
}
20.
21.
public
class
XmlServiceModule : XmlModuleContext
22.
{
23.
private
static
readonly XmlServiceModule instance =
new
XmlServiceModule();
24.
25.
protected
readonly XmlModule module =
null
;
26.
27.
public
XmlServiceModule()
28.
{
29.
var document = XDocument.Load(
'Config/NinjectServiceModule.config'
);
30.
module =
new
XmlModule(document.Element(
'module'
), elementHandlers);
31.
module.OnLoad(kernel);
32.
}
33.
34.
public
static
IKernel GetKernel()
35.
{
36.
return
instance.kernel;
37.
}
38.
}
39.
}
(3)调用服务
01.
/// <summary>
02.
/// 通过xml配置注入
03.
/// </summary>
04.
static
void
InjectByConfig()
05.
{
06.
var kernel = XmlServiceModule.GetKernel();
07.
var logger = kernel.Get<ILogService>();
08.
Console.WriteLine(logger.GetType());
09.
logger.AppendLog(
'hello world'
);
10.
11.
var weapon = kernel.Get<IWeapon>();
12.
Console.WriteLine(weapon.GetType());
13.
Console.WriteLine(weapon.Name);
14.
15.
var warrior = kernel.Get<IWarrior>();
16.
Console.WriteLine(warrior.GetType());
17.
}
说了这么多Ninject,主要还是想将其用到ASP.NET MVC的IOC中,其实很简单,大概就几个步骤搞定:
01.
using System;
02.
using System.Collections.Generic;
03.
using System.Linq;
04.
using System.Web;
05.
using System.Web.Mvc;
06.
using System.Web.Routing;
07.
using Ninject;
08.
using NinjectMvc.Models;
09.
10.
namespace NinjectMvc.Ioc
11.
{
12.
public
class
NinjectControllerFactory : DefaultControllerFactory
13.
{
14.
private
IKernel ninjectKernel;
15.
16.
public
NinjectControllerFactory()
17.
{
18.
ninjectKernel =
new
StandardKernel();
19.
AddBindings();
20.
}
21.
22.
protected
override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
23.
{
24.
return
controllerType ==
null
?
null
: (IController)ninjectKernel.Get(controllerType);
25.
}
26.
27.
private
void
AddBindings()
28.
{
29.
ninjectKernel.Bind<IStudentRepository>().To<StudentRepository>();
30.
}
31.
}
32.
}
01.
public
class
MvcApplication : System.Web.HttpApplication
02.
{
03.
protected
void
Application_Start()
04.
{
05.
AreaRegistration.RegisterAllAreas();
06.
07.
WebApiConfig.Register(GlobalConfiguration.Configuration);
08.
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
09.
RouteConfig.RegisterRoutes(RouteTable.Routes);
10.
BundleConfig.RegisterBundles(BundleTable.Bundles);
11.
AuthConfig.RegisterAuth();
12.
13.
ControllerBuilder.Current.SetControllerFactory(
new
NinjectControllerFactory());
14.
//设置控制器工厂生产类为自己定义的NinjectControllerFactory
15.
}
16.
}
(1)首先声明一个Student学生类
01.
using System;
02.
using System.Collections.Generic;
03.
using System.Linq;
04.
using System.Web;
05.
06.
namespace NinjectMvc.Models
07.
{
08.
public
class
Student
09.
{
10.
public
int
Id { get; set; }
11.
public
string Name { get; set; }
12.
public
string Graduation { get; set; }
13.
public
string School { get; set; }
14.
public
string Major { get; set; }
15.
}
16.
}
(2)然后声明仓储接口和其实现
01.
using System;
02.
using System.Collections.Generic;
03.
using System.Linq;
04.
using System.Text;
05.
06.
namespace NinjectMvc.Models
07.
{
08.
public
interface
IStudentRepository
09.
{
10.
IEnumerable<Student> GetAll();
11.
Student Get(
int
id);
12.
Student Add(Student item);
13.
bool Update(Student item);
14.
bool Delete(
int
id);
15.
}
16.
}
01.
using System;
02.
using System.Collections.Generic;
03.
using System.Linq;
04.
using System.Web;
05.
06.
namespace NinjectMvc.Models
07.
{
08.
public
class
StudentRepository : IStudentRepository
09.
{
10.
private
List<Student> Articles =
new
List<Student>();
11.
12.
public
StudentRepository()
13.
{
14.
//添加演示数据
15.
Add(
new
Student { Id =
1
, Name =
'张三'
, Major =
'软件工程'
, Graduation =
'2013年'
, School =
'西安工业大学'
});
16.
Add(
new
Student { Id =
2
, Name =
'李四'
, Major =
'计算机科学与技术'
, Graduation =
'2013年'
, School =
'西安工业大学'
});
17.
Add(
new
Student { Id =
3
, Name =
'王五'
, Major =
'自动化'
, Graduation =
'2013年'
, School =
'西安工业大学'
});
18.
}
19.
/// <summary>
20.
/// 获取全部学生信息
21.
/// </summary>
22.
/// <returns></returns>
23.
public
IEnumerable<Student> GetAll()
24.
{
25.
return
Articles;
26.
}
27.
/// <summary>
28.
/// 通过ID获取学生信息
29.
/// </summary>
30.
/// <param name='id'></param>
31.
/// <returns></returns>
32.
public
Student Get(
int
id)
33.
{
34.
return
Articles.Find(p => p.Id == id);
35.
}
36.
/// <summary>
37.
/// 添加学生信息
38.
/// </summary>
39.
/// <param name='item'></param>
40.
/// <returns></returns>
41.
public
Student Add(Student item)
42.
{
43.
if
(item ==
null
)
44.
{
45.
throw
new
ArgumentNullException(
'item'
);
46.
}
47.
Articles.Add(item);
48.
return
item;
49.
}
50.
/// <summary>
51.
/// 更新学生信息
52.
/// </summary>
53.
/// <param name='item'></param>
54.
/// <returns></returns>
55.
public
bool Update(Student item)
56.
{
57.
58.
if
(item ==
null
)
59.
{
60.
throw
new
ArgumentNullException(
'item'
);
61.
}
62.
63.
int
index = Articles.FindIndex(p => p.Id == item.Id);
64.
if
(index == -
1
)
65.
{
66.
return
false
;
67.
}
68.
Articles.RemoveAt(index);
69.
Articles.Add(item);
70.
return
true
;
71.
}
72.
/// <summary>
73.
/// 删除学生信息
74.
/// </summary>
75.
/// <param name='id'></param>
76.
/// <returns></returns>
77.
public
bool Delete(
int
id)
78.
{
79.
Articles.RemoveAll(p => p.Id == id);
80.
return
true
;
81.
}
82.
}
83.
}
(3)最后添加控制器StudentController,并注入依赖代码
01.
using System;
02.
using System.Collections.Generic;
03.
using System.Linq;
04.
using System.Web;
05.
using System.Web.Mvc;
06.
using NinjectMvc.Models;
07.
08.
namespace NinjectMvc.Controllers
09.
{
10.
public
class
StudentController : Controller
11.
{
12.
readonly IStudentRepository repository;
13.
//构造器注入
14.
public
StudentController(IStudentRepository repository)
15.
{
16.
this
.repository = repository;
17.
}
18.
19.
public
ActionResult Index()
20.
{
21.
var data = repository.GetAll();
22.
return
View(data);
23.
}
24.
25.
}
26.
}
(4)最后为控制器StudentController的Index方法添加视图即可,这里不再详述,运行效果如下