Ninject依賴注入(一)

Ninject學習筆記(一)

理解依賴注入

DI概念

依賴注入,或者控制反轉,下降代碼耦合度。Ninject是一個輕量級.NET DI框架。程序員

什麼是DI?

一個例子,木匠伐木,須要工具斧子。正則表達式

 
 
 
 
class Carpenter{ Axe axe = new Axe(); void Lumber() { axe.Cut(); }}

代碼中,木匠依賴於斧子。此時需求變了,木匠買了木鋸,那麼上面的代碼必須重新修改而後進行編譯。再好比木匠比較愛惜工具,決定兩種工具換着用,再好比,木匠決定提升生產率,購置了電鋸等等。做爲程序員來講,若是每次需求變動就從新編碼,那麼你會發現本身深陷沼澤地。編程

DI的出現就是爲了解決這一問題的。它是一種編程方式,依賴關係不須要調用者來管理,統一由框架管理。「不要找咱們,咱們來找你」。緩存

DI是如何工做的?

簡簡單單一句話——對接口編程,而不是對具體實現編程。用抽象元素來實現依賴,而不是具體類,如此一來咱們能夠很容易地替換具體的依賴類而不影響上層的調用組件。框架

 
 
 
 
class ITool{ void Cut();}class Carpenter{ private ITool tool; void Carpenter(ITool tool) { tool = tool; } void Lumber() { tool.Cut(); }}

什麼是DI容器

DI容器是一個注入對象,用來向對象注入依賴性。一個應用中的依賴關係組成一個錯綜複雜的依賴圖。DI容器就是來管理依賴複雜性的。它決定抽象類選擇哪一個實現類來實例化對象。Ninject有兩種定義依賴的方式:ide

  1. xml配置:<bind service="ITool to="Axe"/>
  2. 代碼定義:`Bind ().To ()

使用Ninject

如何使用Ninject

  1. 在VS編輯環境中->右鍵項目->選擇Nuget管理器->搜索Ninject->下載;
  2. 在項目中定義Kernel:var kernel = new StandardKernel()
  3. 經過kernel.Get方法獲取依賴的對象。

Ninject對象生命週期

暫時範圍

默認狀態,Ninject無論理它建立的對象,也就是每次請求都new一個新對象。函數

單例範圍

有兩種方式建立單例工具

  1. 使用單例模式
 
 
 
 
class ConsoleLogger:ILogger{ public static readonly ConsoleLogger Instance = new ConsoleLogger(); private static ConsoleLogger() { // Hiding constructor } public void Log(string message) { Console.WriteLine("{0}: {1}", DateTime.Now, message); }}

而後在Bind方法後調用ToConstant方法指定靜態只讀對象ConsoleLogger.Instance爲常量對象。
kernel.Bind<ILogger>().ToConstant(ConsoleLogger.Instance);學習

  1. 使用InSingletonScope方法——更簡單的方法
    kernel.Bind<ILogger>().To<ConsoleLogger>().InSingletonScope();編碼

  2. 指定某個類爲單例
    kernel.Bind<ConsoleLogger>.ToSelf().InThreadScope();

線程範圍

每個線程只建立一個給定類型的對象。對象的生命週期和線程同樣長。
kernel.Bind<object>().ToSelf().InThreadScope();

請求範圍

用在Web應用程序中很是有用。在相同的請求範圍內獲得一個單例的對象。須要添加Ninject.Web.Common引用。
kernel.Bind<SampleClass>().ToSelf().InRequestScope();

自定義範圍

自定義範圍讓咱們定義咱們本身的範圍,在這個範圍內保持一類型的惟一對象。只要提供的回調方法返回的對象引用是同樣的,Ninject在這個範圍內返回相同的實例。只要返回的對象引用變了,將建立一新的指定類型的對象。建立的對象實例將一直保存在緩存裏,直到返回的範圍對象被垃圾回收器回收。一旦範圍對象被垃圾回收器回收,Ninject建立的全部的對象實例將被從緩存中釋放和處理。
調用InScope方法傳入Lamda表達式定義自定義返回:kernel.Bind<object>().ToSelf().InScope( ctx => User.Current );

自定義範圍是最靈活的,能夠實現其餘的範圍:

  1. 線程範圍:kernel.Bind<object>().ToSelf().InScope( ctx => Thread.CurrentThread);
  2. 請求範圍:kernel.Bind<object>().ToSelf().InScope( ctx => HttpContext.Current);

Ninject模塊

若是應用程序規模比較大,那麼註冊的服務列表將會很是長,維護變得困難。一種好的方式是進行分組管理。Ninject提供了這個功能。每一個組稱爲一個Ninject模塊,只須要編寫一個類實現INinjectModule接口,須要實現三個方法和兩個屬性。好消息是,Ninject還提供了一個實現該接口的抽象類NinjectModule,無需每次都實現接口的全部方法。
將多個模塊加載到單個Ninject Kernel中的方法:
var kernel = new StandardKernel(new Module1(), new Module2(), ...)
也能夠將應用程序中全部的模塊同時加載到Ninject Kernel中:
kernel.Load(AppDomain.CurrentDomain.GetAssemblies());

從xml配置依賴(Ninject XML擴展)

須要Ninject XML擴展引用。注意記得發佈xml文件時選擇「Copy if newer」。
XML配置文件格式以下:

 
 
 
 
<module name="moduleName">  <bind service="Namespace.IService1, AssemblyName"    to="Namespace.ConcreteService1, AssemblyName" />  <bind service="Namespace.IService2, AssemblyName"    to="Namespace.ConcreteService2, AssemblyName"    Scope="singleton"/></module>

加載XML文件到Kernel的方法:
kernel.Load("module1.xml","module2.xml","module3.xml");
能夠使用相對輸出路徑的路徑,也能夠使用絕對路徑,還能夠使用通配符:
kernel.Load("Modules/*.xml);

Ninject約定(Ninject Convention擴展)

小的應用中,一個一個註冊服務類型並不困難,可是一個有上百個服務的應用程序呢?約定配置容許咱們綁定一組服務,而不是一個個分別綁定。

註冊一個約定綁定須要三個步驟:選擇包含具體類的程序集、選擇程序集中的具體組件、選擇具體組件相關的服務類型。

選擇程序集

  1. FromThisAssembly():選擇包含當前代碼的程序集;
  2. From(params Assembly[] assemblies):選擇指定程序集;
  3. FromAssemblyContaining<SomeType>():選擇包含指定類的程序集;
  4. Join():選擇多個程序集。
 
 
 
 
kernel.Bind(x => x.FromAssemblyContaining<CustomersService>().SelectAllClasses().Join().FromAssemblyContaining<MessageProvider>().SelectAllClasses().BindAllInterfaces());

默認狀況下只有公有類型能夠在程序集中被邦迪。爲包含非公有類型,須要在選擇程序集後顯式調用IncludingNonePublicTypes方法:

 
 
 
 
kernel.Bind(x => x.FromAssemblyContaining<CustomersService>().IncludingNonePublicTypes().SelectAllClasses().BindAllInterfaces());

選擇組件

選擇要註冊的組件。

  1. SelectAllClasses():選擇全部的非抽象類;
  2. Select(Func<Type, bool> filter):選擇須要的類。
    例子:選擇以「Customer"開頭的全部類:
 
 
 
 
kernel.Bind(r => r.FromThisAssembly().Select(t =>t.Name.StartsWith("Customer")).BindBase());

例子:用條件對結果進行過濾:

 
 
 
 
kernel.Bind(x => x.FromThisAssembly().SelectAllClasses().InNamespaces("Northwind.Controllers").BindBase());

選擇服務類型

  • BindAllInterfaces(): 綁定全部的選擇的組件的接口到選擇的組件。
  • BindBase(): 綁定選擇的組件的基類型到當前的組件。
  • BindDefaultInterface(): 綁定指定類型的默認接口到類型。類型的默認接口跟類型同名。例如,ICustomerService是CutomerService的默認接口。
  • BindDefaultInterfaces(): 綁定指定類型的默認接口到類型。類型的默認接口是那些以類型的名字結尾的接口。例如,IRepository和ICustomerRepository都是SqlCustomerRepository的默認接口。
  • BindSingleInterface(): 要求指定類型只有一個接口。在這個狀況下,這個接口綁定到這個類型。若是這個類型沒有或者有多個接口,則不添加綁定。
  • BindToSelf(): 綁定類型到自身。
  • BindSelection(ServiceSelector selector): 綁定選擇的接口到類型。
  • BindUsingRegex(string pattern): 綁定當前類型的符合正則表達式的接口到類型。

綁定配置

綁定建立後,能夠像普通綁定的配置同樣進行配置:

 
 
 
 
kernel.Bind(x => x.FromThisAssembly().SelectAllClasses().BindAllInterfaces().Configure(b=>b.InSingletonScope()));

咱們也能夠使用ConfigureFor 方法對某些類型分別進行配置。下面的例子,全部的repository類在構造函數中都注入"connectionString"參數,配置成單例生命週期。SqlCustomerRepository類重載成線程生命週期:

 
 
 
 
kernel.Bind(x => x.FromThisAssembly().SelectAllClasses().InheritedFrom<IRepository>().BindAllInterfaces().Configure(b =>b.InSingletonScope ().WithConstructorArgument("connectionString", ApplicationSettings.ConnectionString)).ConfigureFor<SqlCustomerRepository>(b =>b.InThreadScope()));


相關文章
相關標籤/搜索