上一篇說了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請求的各個對象的依賴項(每請求以執行個體) |