1. 程式人生 > >ASP.NET Core技術研究-探祕依賴注入框架

ASP.NET Core技術研究-探祕依賴注入框架

ASP.NET Core在底層內建了一個依賴注入框架,通過依賴注入的方式註冊服務、提供服務。依賴注入不僅服務於ASP.NET Core自身,同時也是應用程式的服務提供者。

毫不誇張的說,ASP.NET Core通過依賴注入實現了各種服務物件的註冊和建立,同時也實現了面向抽象的程式設計模式和程式設計體驗,提升了應用程式的擴充套件性。

今天,我們普及一下ASP.NET Core中依賴注入的一些基本知識。

一、服務的註冊

   我們通過建立一個ASP.NET Core的專案,可以發現在Startup.cs 類中,有一個方法ConfigureServices,這個方法的註釋是這樣的:

     This method gets called by the runtime. Use this method to add services to the container.

   

  在ConfigureServices方法中我們可以將通過ASP.NET Core內建的依賴注入框架實現服務的的註冊。

  這個方法有個引數:IServiceCollection,見名知意,服務集合。

  ASP.NET Core內建的依賴注入框架將服務註冊資訊儲存到一個實現了IServiceCollection介面的物件中。預設情況下這個介面的實現類是ServiceCollection,以下是這個類的說明:

  https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollection?view=dotnet-plat-ext-3.1

  通過這個介面和類實現,我們可以發現,註冊服務其實就是將一個服務的ServiceDescriptor物件新增到ServiceCollection集合中。

  例如:

public void ConfigureServices(IServiceCollection services)
{
    services.Add(new ServiceDescriptor(typeof(IUserRepository), new UserRepository()));
    services.AddControllers();
}

   ServiceDescriptor可以理解為對某個服務註冊項的描述。ASP.NET Core的依賴注入容器IServiceProvider通過ServiceDescriptor的資訊,動態建立服務的例項Instance. 

   我們看一下這個ServiceDescriptor類:

   

   有幾個關鍵的屬性:

     1. ServiceType:服務的型別,例如服務介面的型別資訊

     2. ImplementationType:服務的實現型別,例如服務介面實現類的型別資訊

     3. ImplementationInstance:實現服務的例項,一般是服務單例模式場景下使用。 https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicedescriptor.-ctor?view=dotnet-plat-ext-3.1#Microsoft_Extensions_DependencyInjection_ServiceDescriptor__ctor_System_Type_System_Object_

    4. Lifetime:服務生命週期:Scoped(同一個請求中同一個IServiceProvider提供的物件是同一個)、Singleton(單例)、Transient(每次從服務容器進行請求時建立)

    5. ImplementationFactory 服務例項建立工廠,自定義的IServiceProvider服務提供容器

  服務註冊提供了一系列過載的方法,大家可以根據需要進行選擇:

  

     服務註冊的過程中,涉及到了服務的生命週期的概念,接下來我們詳細看一下。

二、服務生命週期

   服務的生命週期設定,決定了服務提供容器IServiceProvider使用什麼樣的方式提供服務例項物件。正如上面第一章節所說的,

   ASP.NET Core服務依賴注入框架,支援三種類型的服務生命週期:

  •    Singleton
  •    Scoped
  •    Transient

  其中:

   Transient:暫時的,每次從服務容器進行請求時建立。 這種生存期適合輕量級、 無狀態的服務。

   Singleton:單一例項,在第一次請求時(或者在執行 Startup.ConfigureServices 並且使用服務註冊指定例項時)建立的。每個後續請求都使用相同的例項。

   Scoped:範圍內的,作用域生存期服務,以每個客戶端請求(連線)一次的方式建立。可以這麼理解:同一個請求中同一個IServiceProvider提供的物件是同一個。

   微軟給了個例子不錯:先註冊服務,三種類型

public void ConfigureServices(IServiceCollection services)
{    
services.AddRazorPages();
services.AddScoped<IMyDependency, MyDependency>(); services.AddTransient<IOperationTransient, Operation>(); services.AddScoped<IOperationScoped, Operation>(); services.AddSingleton<IOperationSingleton, Operation>(); services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty)); //OperationService depends on each of the other Operation types. services.AddTransient<OperationService, OperationService>(); }

  第一個請求:       

控制器操作:
暫時性:d233e165-f417-469b-a866-1cf1935d2518
作用域:5d997e2d-55f5-4a64-8388-51c4e3a1ad19
單一例項:01271bc1-9e31-48e7-8f7c-7261b040ded9
例項:00000000-0000-0000-0000-000000000000

OperationService 操作:
暫時性:c6b049eb-1318-4e31-90f1-eb2dd849ff64
作用域:5d997e2d-55f5-4a64-8388-51c4e3a1ad19
單一例項:01271bc1-9e31-48e7-8f7c-7261b040ded9
例項:00000000-0000-0000-0000-000000000000

  第二個請求:

第二個請求:
控制器操作:
暫時性:b63bd538-0a37-4ff1-90ba-081c5138dda0
作用域:31e820c5-4834-4d22-83fc-a60118acb9f4
單一例項:01271bc1-9e31-48e7-8f7c-7261b040ded9
例項:00000000-0000-0000-0000-000000000000

OperationService 操作:
暫時性:c4cbacb8-36a2-436d-81c8-8c1b78808aaf
作用域:31e820c5-4834-4d22-83fc-a60118acb9f4
單一例項:01271bc1-9e31-48e7-8f7c-7261b040ded9
例項:00000000-0000-0000-0000-000000000000

   大家可以根據實際的需要選擇服務的生命週期,建立不同型別的服務。

三、服務的消費

  前面,我們將服務註冊到IServiceCollection,ASP.NET Core服務提供容器IServiceProvider就可以根據IServiceCollection 建立具體型別的服務物件了。

  我們先看一下IServiceProvider介面,可以發現:只有一個GetService方法。

  

 

  我們可以通過以下程式碼使用:

 public static void Main(string[] args)
 {
      var builder = CreateHostBuilder(args);            
      var host = builder.Build();

      var userRepo = host.Services.GetService(typeof(IUserRepository)) as IUserRepository;
      userRepo.AddUser("user");

      host.Run();
 }

 同時,我們更多常用的是:

 將服務通過ASP.NET Core依賴注入框架注入到控制器中

 ASP.NET Core MVC 控制器通過建構函式顯式請求依賴關係。即:通過建構函式注入服務的實現。

 前面,我們通過ConfigureServices註冊了服務IUserRepository,在Controller這一層如何消費使用這個服務呢?答案就是在Controller建構函式中注入。

 看一段示例程式碼:(HomeController的建構函式中,增加了一個引數IUserRepository)

  public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;     
        private IUserRepository _userRepository;

        public HomeController(ILogger<HomeController> logger, IUserRepository userRepository)
        {
            _logger = logger;
            _userRepository = userRepository;
        }

        public IActionResult Index()
        {
            _userRepository.AddUser(new User() {  });

            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }

  同時,ASP.NET Core MVC 控制器支援通過註解FromServicesAttribute, 將服務直接注入到Action方法中,而無需使用建構函式注入:

public IActionResult Index([FromServices] IUserRepository userRepository)
{
     userRepository.AddUser(new User() {  });

     return View();
}

  ASP.NET Core除了支援將服務注入到控制器,同時還支援將服務依賴注入到檢視,可以參考以下連結:

    https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/dependency-injection?view=aspnetcore-3.0

    以上是對ASP.NET Core依賴注入框架的研究,分享給大家。

 

  周國慶

 2020/4/12