天天看點

ASP.NET MVC 5 (七-2) Ninject進階建立依賴項鍊指定屬性和構造器參數使用條件綁定設定對象作用域

上一篇說了Ninject的初步使用,這裡在說一下稍微複雜的使用。

  • 建立依賴項鍊
  • 指定屬性和構造器參數
  • 使用條件綁定
  • 設定對象作用域

建立依賴項鍊

依賴項是什麼呢?簡單來說,上一篇當中Home控制器的構造器(構造函數)有IValueCalculator類型參數,那麼IValueCalculator就是HomeConreoller的依賴項了。

當我們的依賴項又依賴于其他類型時,即依賴項又有依賴項時,這時Ninject又會繼續解析并建立所需要的所有類的執行個體,一個又一個的依賴項的依賴性連結成一條依賴項鍊。比如,IValueCalculator的實作類LinqValueCalculator如果又一個依賴項IDiscountHelper,而IDiscountHelper的實作類是DefaultDiscountHelper類,這時就構成了一個依賴項。

為了示範這一過程,我們在Models檔案夾内添加一個Discount.cs檔案,修改其内容如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace EssentialTools.Models
{
    public interface IDiscountHelper
    {
        decimal ApplyDiscount(decimal totalParam);
    }
    public class DefaultDiscountHelper : IDiscountHelper
    {
        public decimal ApplyDiscount(decimal totalParam)
        {
            return (totalParam - (discountSize / 100m * totalParam));
        }
    }
}
           

Discount檔案内定義了IDiscountHelper接口,它的ApplyDiscount方法将一個十進制接口運用于一個十進制的數。DefaultDiscountHelper 類實作了該接口,并使用固定的10%的折扣。

然後我們為LinqValueCalculator類添加一個IDiscountHelper依賴項:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace EssensialTools.Models
{
    public class LinqValueCalculator:IValueCalculator
    {
        private IDiscountHelper discount;
        public LinqValueCalculator(IDiscountHelper discountParam)
        {
            discount = discountParam;
        }
        public decimal ValueProducts(IEnumerable<Product> products)
        {
            return discount.ApplyDiscount(products.Sum(p=>p.Price));
        }
    }
}
           

此時向之前做的那樣,将DefaultDiscountHelper 和IDiscountHelper的綁定關系放到之前的依賴項解析器的AddBindings方法當中就大功告成了。

private void AddBindings()
        {
            kernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
            kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>;
        }
           

這時我們将這個折扣便應用到了計算總價當中了,這構成了一個依賴項鍊。

指定屬性和構造器參數

在将接口和實作進行綁定時,可以為實作類的屬性提供一些值。比如,在Discount.cs檔案對DefaultDiscountHelper 類添加一個屬性:

public class DefaultDiscountHelper : IDiscountHelper
    {
        public decimal DiscountSize { get; set; }
        public decimal ApplyDiscount(decimal totalParam)
        {
            return (totalParam - (DiscountSize / 100m * totalParam));
        }
    }
           

discountSize屬性提供不同的折扣值,在對DefaultDiscountHelper和IDiscountHelper接口進行綁定的時候可以給這個屬性指派,修改AddBindings方法如下:

private void AddBindings()
{
    kernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
    kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize",50M);
}
           

這樣就可以在計算總價時使用50%的折扣了。

當有多個屬性時可以連結帶哦用WithConstructorArgument方法涵蓋所有屬性。

當然也可以使用構造器參數來實作參數傳遞,對DefaultDiscountHelper 類做如下修改:

public class DefaultDiscountHelper : IDiscountHelper
    {
        public decimal discountSize;
        public decimal ApplyDiscount(decimal totalParam)
        {
            return (totalParam - (discountSize / 100m * totalParam));
        }
        public DefaultDiscountHelper(decimal discountParma)
        {
            discountSize = discountParma;
        }
    }
           

這樣可以在依賴項解析器當中使用WithConstructorArgument方法為構造器參數指派

private void AddBindings()
 {
    kernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
    kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithConstructorArgument("discountParam", 50M);
  }
           

使用條件綁定

在Models檔案夾下添加一個FiexibleDiscountHelper類,它實作IDiscount接口,并按照總價給出不同的折扣值。内容如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace EssensialTools.Models
{
    public class FlexibleDiscountHelper : IDiscountHelper
    {
        public decimal ApplyDiscount(decimal totalParam)
        {
            decimal discount = totalParam > 100 ? 70 : 50;
            return (totalParam - (discount / 100m * totalParam));
        }
    }
}
           

此時IDiscount接口有兩個實作方法,在依賴項解析器中我們可以按照不同條件綁定不同的實作類,在AddBindings方法中作出如下修改:

private void AddBindings()
 {
   kernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
   kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithConstructorArgument("discountParam", 50M);
   kernel.Bind<IDiscountHelper>().To<FlexibleDiscountHelper>().WhenInjectedInto<LinqValueCalculator>();
 }
           

這樣可使當要建立一個LinqValueCalculator對象時使用FlexibleDiscountHelper類作為IDiscountHelper接口的實作類,而不是預設的DefaultDiscountHelper實作類。

Ninject有一些綁定條件的方法:

方法 效果
When(謂詞) 當‘謂詞’(一個lambda表達式)的結果為true時,實施綁定
WhenClassHas() 當被注入的類以T類型注解屬性進行注釋時,實施綁定
WhenInjectInto() 當要被注入的類時烈性T時,實施綁定

設定對象作用域

首先在NinjectDependencyResolver類中引入

using Ninject.Web.Common;

命名空間。

此時對AddBindings方法做如下修改:

private void AddBindings()
{
    kernel.Bind<IValueCalculator>().To<LinqValueCalculator>().InRequestScope();
    ...//其他方法未給出
 }
           

綁定的最後使用

InRequestScope

方法指定LinqValueCalculator對象的作用域為一個請求,即一個http請求隻會建立一個對象而不管有幾個依賴項對象。

比如修改Home控制器的初始化器:

public HomeController(IValueCalculator calcParam, IValueCalculator calc2)
{
            calc = calcParam;
}
           

它雖然請求兩個IValueCalculator 對象,但MVC隻會建立**一個**IValueCalculator 的實作類LinqValueCalculator的對象。

以下是指定其他作用域的方法:

-摘自《精通ASP.NET MVC5》(Adam FreeMan)

名稱 效果
InTransientScope() 與未指定作用域相同,為每一個被解析的依賴項建立一個新的對象(每依賴項一執行個體)

InSingeltonScope()

ToConstant(object)

建立一個單一執行個體,使其應用于整個應用程式(每應用一執行個體)
InThreadScope() 建立一個單一執行個體,将其用于解析一個線程中各個對象的執行個體(每線程一執行個體)
InRequestScope() 建立一個單一執行個體,用于解析一個HTTP請求的各個對象的依賴項(每請求以執行個體)

繼續閱讀