ASP.NET MVC IOC 之Ninject攻略

1、爲何要使用Ninject?

不少其它類型的IOC容器過於依賴配置文件,總是配置,總感受有點不爽,並且要使用assembly-qualified名稱(也就是類型的全名)來進行定義,稍不注意就會由於打錯字而令整個程序崩掉。Ninject是一個快如閃電、超輕量級的基於.Net平臺的IOC容器,主要用來解決程序中模塊的耦合問題,它的目的在於作到最少配置。所以若是你不喜歡配置,不喜歡重量級IOC框架,那麼就用小蘋果Ninject吧!git

2、Ninject的使用

首先你必須獲取Ninject,其官網下載地址:http://www.ninject.org,你也能夠經過VS中的NuGet來加載Nniject,不管是哪一種方式,最終的目的就是將 Ninject.dll 這個程序集引用到你的項目中。這樣在你的項目中,若是想使用Ninject,只需添加其命名空間引用便可~github

using Ninject;

一、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()而實現依賴注入,注意這裏用的是代碼的形式!ide

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進行依賴注入就是這麼爽歪歪~測試

二、Ninject經常使用方法屬性說明

這裏咱們使用構造函數注入一個栗子:ui

首先新建一個測試接口ITester與其實現類NinjectTester ,顯然NinjectTester依賴於ILoggerthis

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的方式實現依賴注入的過程當中用到的一些東東~spa

(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有兩個構造函數:intobject類型的。如今咱們已經爲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 "從數據中讀取消息號!";
        }
    }

 三、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>
View Code

(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;
        }
    }
}
View Code

(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());
        }
View Code

3、ASP.NET MVC與Ninject

 說了這麼多Ninject,主要仍是想將其用到ASP.NET MVC的IOC中,其實很簡單,大概就幾個步驟搞定:

一、繼承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>();
        }
    }
}

二、在函數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
        }
    }

三、如今在你的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;
        }
    }
}
View Code

(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方法添加視圖便可,這裏再也不詳述,運行效果以下

 

總結

總的感受來講,Ninject這個IOC容器給人的感受仍是蠻清爽的,我以爲它就是IOC裏邊的小清新,用簡單的方式就可以處理複雜的系統依賴問題,並且它還可以擴展,所以我以爲從ASP.NET MVC 4日後開始它可能真正成爲主流Ioc神器,關於Ninject的一些更深層次和更高級的功能有待之後進一步實踐和研究,無論怎樣,我以爲讓它在你的項目裏跑起來纔是王道,由於這纔是它存在的意義!
相關文章
相關標籤/搜索