Ninject簡介


1.為什么要用Ninject?

Ninject是一個IOC容器用來解決程序中組件的耦合問題,它的目的在於做到最少配置。其他的的IOC工具過於依賴配置文件,需要使用assembly-qualified名稱來進行定義,庸長且復雜常常因為打錯字而破壞程序。這些是他的優點,也是為什么要選擇它。Ninject同時不能進行熱插拔。

 

2.Ninject做些什么?

其實Ninject做的事情很簡單,說白了就是為我們選擇一個想要的類來處理事務。來看下面的簡單的例子。

    public class Product
    {
        public int ProductID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public string Category { get; set; }
    }

    public interface IValueCalculater
    {
        decimal ValueProducts(params Product[] products);
    }

    public class LinqValueCalculator : IValueCalculater
    {
        public decimal ValueProducts(params Product[] products)
        {
            return products.Sum(p => p.Price);
        }
    }

我們定義了一個實體類,一個接口,一個實現接口的類,這個類完成的功能是計算總價。

    public class ShoppingCart
    {
        protected IValueCalculater calculator;
        protected Product[] products;

        public ShoppingCart(IValueCalculater calcuParam)
        {
            this.calculator = calcuParam;
            products = new[]{
                                new Product(){Name="Kayak" , Price=275M},
                                new Product(){Name="Lifejacket" , Price=48.95M},
                                new Product(){Name="Scooceer ball" , Price=19.5M},
                                new Product(){Name="Stadium" , Price=79550M}
                            };
        }

        public virtual decimal CalculatStockValue()
        {
            decimal totalPrice = calculator.ValueProducts(products);
            return totalPrice;
        }
    }

ShopingCart類的構造函數使用接口IValueCalculater實現作為一個准備DI的參數。CalculatStockValue方法創建一個Product對象數組,然后調用IValueCalculater接口中的ValueProducts來獲得總價。這里可以看到ShoppingCart類中只出現了接口IValueCalculater,卻沒有出現這個接口的實現類LinqValueCalculator,說的直白點就是ShoppingCart中計算的總價是一個影藏的類實現的,我們可以修改這個影藏的類達到修改總價的目的,而不需要修改ShoppingCart類中的代碼。好的,Ninject做的工作就是和上面的類似,是的,它只能完成這么個工作,不過會有很多的變化,最終目的只有一個,就是決定到當前到底要使用哪一個類似LinqValueCalculator着那個的實現類。

 

3.Ninject的使用

關於如何下載安裝Ninject這里不再說了,網上有很多的資源。這里只說幾個簡單的例子。

            IKernel ninjectKernel = new StandardKernel();
            ninjectKernel.Bind<IValueCalculater>().To<LinqValueCalculator>();

上面的代碼將想使用的類型和他的接口進行綁定,高祖Ninject,當接收到一個實現IValueCalculater的請求的時候,創建病返回LinqValueCalculator這個類,上面的兩個關鍵字Bind,To可以幫助我們理解他的意思。

IValueCalculater calcImpl = ninjectKernel.Get<IValueCalculater>();
ShoppingCart cart = new ShoppingCart(calcImpl);

當我們調用上面兩句的時候就會去計算總價。

下面我們看看他的變形。

1.依賴性鏈

當使用ninjectKernel.Get創建一個類型的時候會檢查這個類型與其他類型之間的耦合,如果有額外的依賴性,Ninject會解析這些依賴性,並創建所需要的所有的類的類型。下面我們在LinqValueCalculator中再次依賴其他的類型。

    public interface IDiscountHelper
    {
        decimal ApplyDiscount(decimal totalParam);
    }

    public class DefalutDiscountHelper : IDiscountHelper
    {
        private decimal discountRate;

        public DefalutDiscountHelper(decimal discountParam)
        {
            discountRate = discountParam;
        }

        public decimal ApplyDiscount(decimal totalParam)
        {
            return (totalParam - (discountRate / 100M * totalParam));
        }
    }

    public class LinqValueCalculator : IValueCalculater
    {
        IDiscountHelper discounter;

        public LinqValueCalculator(IDiscountHelper discountParam)
        {
            discounter = discountParam;
        }

        public decimal ValueProducts(params Product[] products)
        {
            return discounter.ApplyDiscount(products.Sum(p => p.Price));
        }
    }

和IValueCalculator所做的那樣我們把IDiscountHelperDefalutDiscountHelper 關聯起來。

ninjectKernel.Bind<IDiscountHelper>().To<DefalutDiscountHelper>();
IValueCalculater calcImpl = ninjectKernel.Get<IValueCalculater>();

IValueCalculater被請求的時候,Ninject知道它要實例化的是LinqValueCalculator,然后進一步考察這個類,並發現他依賴一個可以解析的接口,Ninject會創建DefaultDiscountHelper的一個實例,並把它注入到LinqValueCalculator類的構造器中,一IValueCalculator作為返回結果,不管這個依賴性鏈有多長,多復雜,Ninject都會以這種方式檢查它要實例化的每一個依賴性。

2.指定屬性和參數值

修改DefalutDiscountHelper 類如下

    public class DefalutDiscountHelper : IDiscountHelper
    {
        private decimal discountRate;

        public decimal DiscountSize { get; set; }



        public decimal ApplyDiscount(decimal totalParam)
        {
            return (totalParam - (DiscountSize / 100M * totalParam));
        }
    }

在使用Ninject將具體類綁定到類型的時候,我已使用WithPropertyValue方法來設置DefalutDiscountHelper 類中的屬性DiscountSize,方法如下

            ninjectKernel.Bind<IDiscountHelper>().To<DefalutDiscountHelper>()
                                                 .WithPropertyValue("DiscountSize", 50M);
IValueCalculater calcImpl = ninjectKernel.Get<IValueCalculater>();

3.指定構造函數的參數值

如果具體類中有帶參數的構造函數可以使用WithConstructorArgument方法來指定這個參數,修改DefalutDiscountHelper如下

    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));
        }
    }

使用Ninject綁定如下:

            ninjectKernel.Bind<IDiscountHelper>().To<DefalutDiscountHelper>()
                                                  .WithConstructorArgument("discountParam", 50M);
IValueCalculater calcImpl = ninjectKernel.Get<IValueCalculater>();

4.上面我們都是綁定接口,其實只要是有繼承關系的兩個類之間也可以進行綁定,下面我們來看一個例子,我們先頂一個ShoppingCart類,然后定義一個LimitShoppingCart類來繼承它,代碼如下:

    public class ShoppingCart
    {
        protected IValueCalculater calculator;
        protected Product[] products;

        public ShoppingCart(IValueCalculater calcuParam)
        {
            this.calculator = calcuParam;
            products = new[]{
                                new Product(){Name="Kayak" , Price=275M},
                                new Product(){Name="Lifejacket" , Price=48.95M},
                                new Product(){Name="Scooceer ball" , Price=19.5M},
                                new Product(){Name="Stadium" , Price=79550M}
                            };
        }

        public virtual decimal CalculatStockValue()
        {
            decimal totalPrice = calculator.ValueProducts(products);
            return totalPrice;
        }
    }
    public class LimitShoppingCart : ShoppingCart
    {
        public decimal ItemLimit { get; set; }

        public LimitShoppingCart(IValueCalculater calcParm)
            : base(calcParm)
        { }

        public override decimal CalculatStockValue()
        {
            var filteredProducts = products.Where(e => e.Price < ItemLimit);
            return calculator.ValueProducts(filteredProducts.ToArray());
        }
    }

下面的代碼將子類綁定到它的父類上,代碼如下:

            ninjectKernel.Bind<ShoppingCart>().To<LimitShoppingCart>().WithPropertyValue("ItemLimit",200M);
            ShoppingCart cart = ninjectKernel.Get<LimitShoppingCart>();

5.使用條件綁定

可以使用Ninject綁定同一個接口的多個實現,或同一個類的多個派生類,並在不同條件下綁定不同的類。我們可以對當前綁定的具體類進行判斷,最終綁定另外一個具體類,先來定義一個IValueCalculator的另外一個實現類,代碼如下

        public decimal ValueProducts(params Product[] products)
        {
            decimal totalValue = 0;

            foreach (Product p in products)
            {
                totalValue += p.Price;
            }
            return totalValue;
        }

下面的代碼將有選擇的創建Ninject類。

ninjectKernel.Bind<IValueCalculater>().To<IterativeValueCalculatgor>().WhenInjectedInto<LimitShoppingCart>();

這段代碼的意思是當LimitShoppingCart這個類被綁定的時候才將IterativeValueCalculatgor綁定到它的接口中,我們可以將IterativeValueCalculatgor和LimitShoppingCart看做是一套具體的邏輯,是有具體關系的,例如這是同一次促銷的產品,即他們是為同一此促銷活動而新建的類,這就為我們的業務邏輯實現提供一個便利。

 

 3.將Ninject用於ASP.NET MVC

 這部分本人還沒有用到過,是從書本中看到的,也是初次接觸。DefaultControllerFactory類是創建控制器類實例的一個類。先看代碼吧。

    public class NinjectControllerFactory : DefaultControllerFactory
    {
        private IKernel ninjectKernel;

        public NinjectControllerFactory()
        {
            ninjectKernel = new StandardKernel();
            AddBindings();
        }

        private void AddBindings()
        {
            ninjectKernel.Bind<IValueCalculater>().To<LinqValueCalculator>();
        }

        protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
        {
            return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType);
        }
    }

這個類創建了一個Ninject內核,用它對控制器類的請求進行服務,請求是通過GetControllerInstance方法實現的,它是在MVC框架在需要一個控制器對象時調用的。我們不需要明確的綁定控制器,可以依靠默認的自身綁定特性,因為控制器是從System.Web.Mvc.Controller派生來的具體類。

AddBinding方法允許我們隊存儲庫和希望保持送耦合的組件添加Ninject綁定,也可以吧這個方法用於對需要額外的構造器參數或者屬性的控制器進行綁定,說的明白點就是我們上面展示的那些需要自己綁定的類。

創建了這個類可以用MVC框架對它進行注冊,在Global.asax類的Application_Start方法中來完成注冊,代碼如下

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
        }

現在我們使用NinjectControllerFactory來獲得控制器的實例,而Ninject將自動第吧DI運用到控制器對象中。

希望這篇簡單的介紹對你有用。


免責聲明!

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



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