Abp vNext框架 基礎知識 依賴注入


依賴注入

ABP的依賴注入系統是基於Microsoft的依賴注入擴展庫(Microsoft.Extensions.DependencyInjection nuget包)開發的.因此,它的文檔在ABP中也是有效的.

雖然ABP框架沒有對任何第三方DI提供程序的核心依賴, 但它必須使用一個提供程序來支持動態代理(dynamic proxying)和一些高級特性以便ABP特性能正常工作.啟動模板中已安裝了Autofac. 更多信息請參閱 Autofac 集成 文檔.

模塊化

由於ABP是一個模塊化框架,因此每個模塊都定義它自己的服務並在它自己的單獨模塊類中通過依賴注入進行注冊.例:

public class BlogModule : AbpModule {     public override void ConfigureServices(ServiceConfigurationContext context)     {         //在此處注入依賴項     } }
C#
 

依照約定的注冊

ABP引入了依照約定的服務注冊.依照約定你無需做任何事,它會自動完成.如果要禁用它,你可以通過重寫PreConfigureServices方法,設置SkipAutoServiceRegistration為true.

public class BlogModule : AbpModule {     public override void PreConfigureServices(ServiceConfigurationContext context)     {         SkipAutoServiceRegistration = true;     } }
C#
 

一旦跳過自動注冊,你應該手動注冊你的服務.在這種情況下,AddAssemblyOf擴展方法可以幫助你依照約定注冊所有服務.例:

public class BlogModule : AbpModule {     public override void PreConfigureServices(ServiceConfigurationContext context)     {         SkipAutoServiceRegistration = true;     }      public override void ConfigureServices(ServiceConfigurationContext context)     {         context.Services.AddAssemblyOf<BlogModule>();     } }
C#
 

以下部分解釋了約定和配置.

固有的注冊類型

一些特定類型會默認注冊到依賴注入.例子:

  • 模塊類注冊為singleton.
  • MVC控制器(繼承Controller或AbpController)被注冊為transient.
  • MVC頁面模型(繼承PageModel或AbpPageModel)被注冊為transient.
  • MVC視圖組件(繼承ViewComponent或AbpViewComponent)被注冊為transient.
  • 應用程序服務(實現IApplicationService接口或繼承ApplicationService類)注冊為transient.
  • 存儲庫(實現IRepository接口)注冊為transient.
  • 域服務(實現IDomainService接口)注冊為transient.

示例:

public class BlogPostAppService : ApplicationService { }
C#
 

BlogPostAppService 由於它是從已知的基類派生的,因此會自動注冊為transient生命周期.

依賴接口

如果實現這些接口,則會自動將類注冊到依賴注入:

  • ITransientDependency 注冊為transient生命周期.
  • ISingletonDependency 注冊為singleton生命周期.
  • IScopedDependency 注冊為scoped生命周期.

示例:

public class TaxCalculator : ITransientDependency { }
C#
 

TaxCalculator因為實現了ITransientDependency,所以它會自動注冊為transient生命周期.

Dependency 特性

配置依賴注入服務的另一種方法是使用DependencyAttribute.它具有以下屬性:

  • Lifetime: 注冊的生命周期:Singleton,Transient或Scoped.
  • TryRegister: 設置true則只注冊以前未注冊的服務.使用IServiceCollection的TryAdd ... 擴展方法.
  • ReplaceServices: 設置true則替換之前已經注冊過的服務.使用IServiceCollection的Replace擴展方法.

示例:

[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] public class TaxCalculator {  }
C#
 

如果定義了Lifetime屬性,則Dependency特性具有比其他依賴接口更高的優先級.

ExposeServices 特性

ExposeServicesAttribute用於控制相關類提供了什么服務.例:

[ExposeServices(typeof(ITaxCalculator))] public class TaxCalculator: ICalculator, ITaxCalculator, ICanCalculate, ITransientDependency {  }
C#
 

TaxCalculator類只公開ITaxCalculator接口.這意味着你只能注入ITaxCalculator,但不能注入TaxCalculator或ICalculator到你的應用程序中.

依照約定公開的服務

如果你未指定要公開的服務,則ABP依照約定公開服務.以上面定義的TaxCalculator為例:

  • 默認情況下,類本身是公開的.這意味着你可以按TaxCalculator類注入它.
  • 默認情況下,默認接口是公開的.默認接口是由命名約定確定.在這個例子中,ICalculator和ITaxCalculator是TaxCalculator的默認接口,但ICanCalculate不是.

組合到一起

只要有意義,特性和接口是可以組合在一起使用的.

[Dependency(ReplaceServices = true)] [ExposeServices(typeof(ITaxCalculator))] public class TaxCalculator : ITaxCalculator, ITransientDependency {  }
C#
 

手動注冊

在某些情況下,你可能需要向IServiceCollection手動注冊服務,尤其是在需要使用自定義工廠方法或singleton實例時.在這種情況下,你可以像Microsoft文檔描述的那樣直接添加服務.例:

public class BlogModule : AbpModule {     public override void ConfigureServices(ServiceConfigurationContext context)     {         //注冊一個singleton實例         context.Services.AddSingleton<TaxCalculator>(new TaxCalculator(taxRatio: 0.18));          //注冊一個從IServiceProvider解析得來的工廠方法         context.Services.AddScoped<ITaxCalculator>(sp => sp.GetRequiredService<TaxCalculator>());     } }
C#
 

注入依賴關系

使用已注冊的服務有三種常用方法.

構造方法注入

這是將服務注入類的最常用方法.例如:

public class TaxAppService : ApplicationService {     private readonly ITaxCalculator _taxCalculator;      public TaxAppService(ITaxCalculator taxCalculator)     {         _taxCalculator = taxCalculator;     }      public void DoSomething()     {         //...使用 _taxCalculator...     } }
C#
 

TaxAppService在構造方法中得到ITaxCalculator.依賴注入系統在運行時自動提供所請求的服務.

構造方法注入是將依賴項注入類的首選方式.這樣,除非提供了所有構造方法注入的依賴項,否則無法構造類.因此,該類明確的聲明了它必需的服務.

屬性注入

Microsoft依賴注入庫不支持屬性注入.但是,ABP可以與第三方DI提供商(例如Autofac)集成,以實現屬性注入.例:

public class MyService : ITransientDependency {     public ILogger<MyService> Logger { get; set; }      public MyService()     {         Logger = NullLogger<MyService>.Instance;     }      public void DoSomething()     {         //...使用 Logger 寫日志...     } }
C#
 

對於屬性注入依賴項,使用公開的setter聲明公共屬性.這允許DI框架在創建類之后設置它.

屬性注入依賴項通常被視為可選依賴項.這意味着沒有它們,服務也可以正常工作.Logger就是這樣的依賴項,MyService可以繼續工作而無需日志記錄.

為了使依賴項成為可選的,我們通常會為依賴項設置默認/后備(fallback)值.在此示例中,NullLogger用作后備.因此,如果DI框架或你在創建MyService后未設置Logger屬性,則MyService依然可以工作但不寫日志.

屬性注入的一個限制是你不能在構造函數中使用依賴項,因為它是在對象構造之后設置的.

當你想要設計一個默認注入了一些公共服務的基類時,屬性注入也很有用.如果你打算使用構造方法注入,那么所有派生類也應該將依賴的服務注入到它們自己的構造方法中,這使得開發更加困難.但是,對於非可選服務使用屬性注入要非常小心,因為它使得類的要求難以清楚地看到.

從IServiceProvider解析服務

你可能希望直接從IServiceProvider解析服務.在這種情況下,你可以將IServiceProvider注入到你的類並使用GetService方法,如下所示:

public class MyService : ITransientDependency {     private readonly IServiceProvider _serviceProvider;      public MyService(IServiceProvider serviceProvider)     {         _serviceProvider = serviceProvider;     }      public void DoSomething()     {         var taxCalculator = _serviceProvider.GetService<ITaxCalculator>();         //...     } }
C#
 

釋放/處理(Releasing/Disposing)服務

如果你使用了構造函數或屬性注入,則無需擔心釋放服務的資源.但是,如果你從IServiceProvider解析了服務,在某些情況下,你可能需要注意釋放服務.

ASP.NET Core會在當前HTTP請求結束時釋放所有服務,即使你直接從IServiceProvider解析了服務(假設你注入了IServiceProvider).但是,在某些情況下,你可能希望釋放/處理手動解析的服務:

  • 你的代碼在AspNet Core請求之外執行,執行者沒有處理服務范圍.
  • 你只有對根服務提供者的引用.
  • 你可能希望立即釋放和處理服務(例如,你可能會創建太多具有大量內存占用且不想過度使用內存的服務).

在任何情況下,你都可以使用這樣的using代碼塊來安全地立即釋放服務:

using (var scope = _serviceProvider.CreateScope()) {     var service1 = scope.ServiceProvider.GetService<IMyService1>();     var service2 = scope.ServiceProvider.GetService<IMyService2>(); }
C#
 

兩個服務在創建的scope被處理時(在using塊的末尾)釋放.

高級特性

IServiceCollection.OnRegistred 事件

你可能想在注冊到依賴注入的每個服務上執行一個操作, 在你的模塊的 PreConfigureServices 方法中, 使用 OnRegistred 方法注冊一個回調(callback) , 如下所示:

public class AppModule : AbpModule {     public override void PreConfigureServices(ServiceConfigurationContext context)     {         context.Services.OnRegistred(ctx =>         {             var type = ctx.ImplementationType;             //...         });     } }
C#
 

ImplementationType 提供了服務類型. 該回調(callback)通常用於向服務添加攔截器. 例如:

public class AppModule : AbpModule {     public override void PreConfigureServices(ServiceConfigurationContext context)     {         context.Services.OnRegistred(ctx =>         {             if (ctx.ImplementationType.IsDefined(typeof(MyLogAttribute), true))             {                 ctx.Interceptors.TryAdd<MyLogInterceptor>();             }         });     } }
C#
 

這個示例判斷一個服務類是否具有 MyLogAttribute 特性, 如果有的話就添加一個 MyLogInterceptor 到攔截器集合中.

注意, 如果服務類公開了多於一個服務或接口, OnRegistred 回調(callback)可能被同一服務類多次調用. 因此, 較安全的方法是使用 Interceptors.TryAdd 方法而不是 Interceptors.Add 方法. 請參閱動態代理(dynamic proxying)/攔截器 文檔.

第三方提供程序

雖然ABP框架沒有對任何第三方DI提供程序的核心依賴, 但它必須使用一個提供程序來支持動態代理(dynamic proxying)和一些高級特性以便ABP特性能正常工作.

啟動模板中已安裝了Autofac. 更多信息請參閱 Autofac 集成 文檔.

 


免責聲明!

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



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