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,只需添加其命名空間引用即可~

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有兩個構造函數: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 "從數據中讀取消息號!";
        }
    }

 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>
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

三、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;
        }
    }
}
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的一些更深層次和更高級的功能有待以后進一步實踐和研究,不管怎樣,我覺得讓它在你的項目里跑起來才是王道,因為這才是它存在的意義!
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM