ASP.NET MVC IOC 之Ninject攻略

  • 一、为什么要使用Ninject?

    很多其它类型的IOC容器过于依赖配置文件,老是配置,总感觉有点不爽,而且要使用assembly-qualified名称(也就是类型的全名)来进行定义,稍不注意就会因为打错字而令整个程序崩掉。Ninject是一个快如闪电、超轻量级的基于.Net平台的IOC容器,主要用来解决程序中模块的耦合问题,它的目的在于做到最少配置。因此如果你不喜欢配置,不喜欢重量级IOC框架,那么就用小苹果Ninject吧!

    二、Ninject的使用

    首先你必须获取Ninject,其官网下载地址:http://www.ninject.org,你也可以通过VS中的NuGet来加载Nniject,不论是哪种方式,最终的目的就是将 Ninject.dll 这个程序集引用到你的项目中。这样在你的项目中,如果想使用Ninject,只需添加其命名空间引用即可~

    view source print ?
    1. using Ninject;

    1、Ninject入门

    我们先定义一个记录日志的接口:

    view source print ?
    1. public interface ILogger
    2. {
    3. void Write(string message);
    4. }

    然后用文件记录日志方式和数据库记录日志方式分别实现这个接口,不过这里只是演示而已,所以并没有真正去实现这两个类,你懂的~

    view source print ?
    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()而实现依赖注入,注意这里用的是代码的形式!

    view source print ?
    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. }

    具体调用方法:

    view source print ?
    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进行依赖注入就是这么爽歪歪~

    2、Ninject常用方法属性说明

    这里我们使用构造函数注入一个栗子:

    首先新建一个测试接口ITester与其实现类NinjectTester ,显然NinjectTester依赖于ILogger

    view source print ?
    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的方式实现依赖注入的过程中用到的一些东东~

    (1)Bind<T1>().To<T2>()

    其实就是接口IKernel的方法,把某个类绑定到某个接口,T1代表的就是接口或者抽象类,而T2代表的就是其实现类

    例如:

    view source print ?
    1. IKernel ninjectKernel = new StandardKernel();
    2. ninjectKernel.Bind<ILogger>().To<FileLogger>();

    (2)Get<ISomeInterface>()

    其实就是得到某个接口的实例,例如下面的栗子就是得到ILogger的实例FileLogger:

    view source print ?
    1. ILogger myLogger= ninjectKernel.Get<ILogger>();

    (3)Bind<T1>() .To<T2>(). WithPropertyValue('SomeProprity', value);

    其实就是在绑定接口的实例时,同时给实例NinjectTester的属性赋值,例如:

    view source print ?
    1. ninjectKernel.Bind<ITester>().To<NinjectTester>().WithPropertyValue('_Message''这是一个属性值注入');

    (4)ninjectKernel.Bind<T1>().To<T2>(). WithConstructorArgument('someParam', value);

    其实就是说我们可以为实例的构造方法所用的参数赋值,例如:

    view source print ?
    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. }
    view source print ?
    1. ninjectKernel.Bind<IDiscountHelper>().To<DefalutDiscountHelper>().WithConstructorArgument('discountParam', 50M);

    (5)Bind<T1>().ToConstant()

    这个方法的意思是绑定到某个已经存在的常量,例如:

    view source print ?
    1. StudentRepository sr = new StudentRepository();
    2. ninjectKernel.Bind<IStudentRepository>().ToConstant(sr);

    (6)Bind<T1>().ToSelf()

    这个方法意思是绑定到自身,但是这个绑定的对象只能是具体类,不能是抽象类。为什么要自身绑定呢?其实也就是为了能够利用Ninject解析对象本身而已。例如:

    view source print ?
    1. ninjectKernel.Bind<StudentRepository>().ToSelf();
    2. StudentRepository sr = ninjectKernel.Get<StudentRepository>();

    (7)Bind<T1>().To<T2>().WhenInjectedInto<instance>()

    这个方法是条件绑定,就是说只有当注入的对象是某个对象的实例时才会将绑定的接口进行实例化

    view source print ?
    1. ninjectKernel.Bind<IValueCalculater>().To<IterativeValueCalculatgor>().WhenInjectedInto<LimitShoppingCart>();

    (8)Bind<T1>().To<T2>().InTransientScope()或者Bind<T1>().To<T2>().InSingletonScope()

     这个方法是为绑定的对象指明生命周期其实

    view source print ?
    1. ninjectKernel.Bind<IStudentRepository>().To<StudentRepository>().InTransientScope();
    2. //每次调用创建新实例
    3. ninjectKernel.Bind<IStudentRepository>().To<StudentRepository>().InSingletonScope();
    4. //每次调用是同一个实例

    (9)Load()方法

     这里的Load()方法其实是抽象类Ninject.Modules.NinjectModule的一个抽象方法,通过重写Load()方法可以对相关接口和类进行集中绑定,例如:

    view source print ?
    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()方法绑定之后的完整代码:

    view source print ?
    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. }

    (10)Inject属性

    在Inject中,我们可以通过在构造函数、属性和字段上加 Inject特性指定注入的属性、方法和字段等,例如下面的栗子,MessageDB有两个构造函数:int和object类型的。现在我们已经为int型的指定了Inject特性,因此在注入的时候选择的就是int型的构造函数;如果没有在构造函数上指定Inject特性,则默认选择第一个构造函数:

    view source print ?
    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. }

     3、Ninject能使用配置文件吗?

    答案是肯定的,要不怎么说Ninject是可扩展的呢,必须可以,但是如果这样的话,你就没必要用Ninject了,因为这样又回到了繁琐的配置上面,还不如用其他的IOC容器来得方便,这里用的是Ninject的XML扩展,你可以通过下面的地址https://github.com/ninject/ninject.extensions.xml下载扩展组件,主要也就是Ninject.Extensions.Xml这个东东起的作用,这里不想详说,给个栗子了结:

    (1)配置文件


    加载中...
    view source print ?
    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)利用扩展加载服务


    加载中...
    view source print ?
    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)调用服务


    加载中...
    view source print ?
    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. }

    三、ASP.NET MVC与Ninject

     说了这么多Ninject,主要还是想将其用到ASP.NET MVC的IOC中,其实很简单,大概就几个步骤搞定:

    1、继承DefaultControllerFactory,重载GetControllerInstance方法,实现自己的NinjectControllerFactory类

    view source print ?
    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. }

    2、在函数Application_Start() 注册自己的控制器工厂类

    view source print ?
    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. }

    3、现在在你的MVC程序中注入依赖代码就ok了

    (1)首先声明一个Student学生类

    view source print ?
    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)然后声明仓储接口和其实现

    view source print ?
    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. }

    加载中...
    view source print ?
    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,并注入依赖代码

    view source print ?
    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方法添加视图即可,这里不再详述,运行效果如下

    ASP.NET MVC IOC 之Ninject攻略_第1张图片

     

    总结

    总的感觉来说,Ninject这个IOC容器给人的感觉还是蛮清爽的,我觉得它就是IOC里边的小清新,用简单的方式就能够处理复杂的系统依赖问题,而且它还能够扩展,因此我觉得从ASP.NET MVC 4往后开始它可能真正成为主流Ioc神器,关于Ninject的一些更深层次和更高级的功能有待以后进一步实践和研究,不管怎样,我觉得让它在你的项目里跑起来才是王道,因为这才是它存在的意义!  

延伸阅读:

你可能感兴趣的:(ASP.NET MVC IOC 之Ninject攻略)