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