ASP.NET Core 依賴注入基本用法

ASP.NET Core 依賴注入

ASP.NET Core從框架層對依賴注入提供支持。也就是說,若是你不瞭解依賴注入,將很難適應 ASP.NET Core的開發模式。本文將介紹依賴注入的基本概念,並結合代碼演示如何在 ASP.NET Core中使用依賴注入。html

什麼是依賴注入?

百度百科對於依賴注入的介紹:web

控制反轉(Inversion of Control,縮寫爲IoC),是面向對象編程中的一種設計原則,能夠用來減低計算機代碼之間的耦合度。其中最多見的方式叫作依賴注入(Dependency Injection,簡稱DI),還有一種方式叫「依賴查找」(Dependency Lookup)。經過控制反轉,對象在被建立的時候,由一個調控系統內全部對象的外界實體將其所依賴的對象的引用傳遞給它。也能夠說,依賴被注入到對象中。編程

從百科的介紹中能夠看出,依賴注入和控制反轉是一回事,依賴注入是一種新的設計模式,經過正確使用依賴注入的相關技術,能夠下降系統耦合度,增長系統的可擴展性。設計模式

咱們來看一個例子:app

public interface IInterfaceA
{ }

public interface IInterfaceB
{ }

public class ClassA : IInterfaceA
{
    private IInterfaceB B { get; set; }

    public ClassA(IInterfaceB b)
    {
        this.B = b;
    }
}

public class ClassB : IInterfaceB
{ }

這個時候,若是咱們想要獲取IInterfaceA的實例,若是不採用依賴注入,咱們的作法一般是這樣的:框架

IInterfaceB b = new ClassB();
IInterfaceA a = new ClassA(b);

這個時候IInterfaceA的控制權,在實例化的時候就已經被限定死了,沒有任何想象空間,就是ClassA的實例,而且咱們還要手工的初始化IInterfaceB,一樣B的控制權也被限定死了。這樣的代碼毫無設計、也極不利於擴展。asp.net

若是採用依賴注入,咱們看一下代碼:ide

var a = container.GetService<IInterfaceA>();

這個時候接口A和B的控制權是由容器來控制的,咱們能夠經過向容器中注入不一樣的接口實現來擴展系統的靈活性,因爲將控制權交給了IoC容器,咱們還能夠經過配置的方式靈活的控制對象的生命週期,這一點也是手工建立對象沒法實現的。函數

控制反轉的關係圖以下(圖片來源於官網):
image測試

ASP.NET Core中的依賴注入

上面介紹了依賴注入的基本概念,那麼在 ASP.NET Core中,咱們該如何使用依賴注入呢?在 ASP.NET Core中已經內置了一套依賴注入的容器,咱們能夠直接使用。

在Startup.ConfigureServices中添加咱們要註冊的服務和實現,添加的時候能夠對服務的生命週期進行相應的配置,而後就能夠在PageModel、Controller、Views等須要的地方使用了。

下面的示例將演示如何註冊服務,代碼來源於官網。首先要定義一個服務接口,並實現這個接口:

public interface IMyDependency
{
    Task WriteMessage(string message);
}

public class MyDependency : IMyDependency
{
    private readonly ILogger<MyDependency> _logger;

    public MyDependency(ILogger<MyDependency> logger)
    {
        _logger = logger;
    }

    public Task WriteMessage(string message)
    {
        _logger.LogInformation(
            "MyDependency.WriteMessage called. Message: {MESSAGE}", 
            message);

        return Task.FromResult(0);
    }
}

而後咱們進行服務註冊:

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyDependency, MyDependency>();
    services.AddMvc();
}

這裏咱們添加了IMyDependency的註冊,同時也添加了使用Mvc所須要的服務的註冊。這裏有兩個問題須要說明:

  • AddScoped是添加一個服務註冊,Scoped是該服務的生命週期,表示按照做用於建立該服務,若是做用域中屢次使用到該服務,則只建立一個對象。好比每個HTTP請求都是一個做用域,那麼在這個請求處理過程當中,容器只會建立一個對象。與Scoped對應的還有其它的生命週期,咱們將服務的生命週期列舉以下:

    • Transient:瞬時服務,表示每次使用都會建立新的對象
    • Scoped:做用域服務,表示每次請求只建立一個對象。這裏須要特殊說明一下,若是你的服務是一箇中間件,不受此約束,由於中間件都是強制單例的。若是要在中間件中使用Scoped服務,則須要將服務注入到Invoke或InvokeAsync方法的參數中,此處能夠參考 ASP.NET Core 中間件基本用法
    • Singleton:單例服務,表示每一個應用程序域只會建立一個實力。
  • 基於約定,ASP.NET Core推薦咱們採用相似於Add{SERVICE_NAME}的方式添加服務的註冊,好比services.AddMvc(),這種方式能夠經過擴展方法來實現,代碼以下:
namespace Microsoft.Extensions.DependencyInjection
{
    public static partial class MyDependencyExtensions
    {
        public static IServiceCollection AddMyDependency(this IServiceCollection services)
        {
            return services.AddScoped<IMyDependency, MyDependency>();
        }
    }
}

使用依賴注入

在瞭解了依賴注入的基本用法以後,咱們如今來了解一下如何將服務注入到Controller、Views中。

在控制器中注入服務

最常規的用法是採用構造函數注入的方式,將一個服務注入到控制器中,代碼以下:

public class DefaultController : Controller
{
    private readonly ILogger<DefaultController> logger;

    public DefaultController(ILogger<DefaultController> logger)
    {
        this.logger = logger;
    }
}

構造函數注入是最經常使用的注入方式,這種方式要求依賴者提供公有的構造函數,並將依賴項經過構造函數的方式傳入依賴者,完成對依賴項的賦值。

除此以外,還能夠經過參數注入的方式,將依賴項注入到Action中,這裏使用到FromServices特性:

public IActionResult Index([FromServices]ILogger<DefaultController> logger)
{
    throw new NotImplementedException();
}

ASP.NET Core 提供了這種支持,可是做者並不推薦這種操做

在視圖中注入服務

ASP.NET Core 支持將依賴關係注入到視圖,代碼以下:

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration
@{
   string myValue = Configuration["root:parent:child"];
   ...
}

上面的代碼演示了將IConfiguration服務注入到視圖中,從而實如今視圖中讀取配置的功能。

有時候將服務注入到視圖中會頗有用(例如本地化),可是做者也並非很推薦這種作法,由於這樣作容易形成視圖和控制器的邊界不清晰。

在PageModel中注入服務

在PageModel中注入服務的方式,與在Controller中注入服務的方式類似:

public class IndexModel : PageModel
{
    private readonly IMyDependency _myDependency;

    public IndexModel(IMyDependency myDependency)
    {
        _myDependency = myDependency;
    }
}

在main方法中獲取服務

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

    using (var serviceScope = host.Services.CreateScope())
    {
        var services = serviceScope.ServiceProvider;

        try
        {
            var serviceContext = services.GetRequiredService<MyScopedService>();
            // Use the context here
        }
        catch (Exception ex)
        {
            var logger = services.GetRequiredService<ILogger<Program>>();
            logger.LogError(ex, "An error occurred.");
        }
    }

    host.Run();
}

在HttpContext.RequestServices中獲取服務

這種方式不利於測試,不推薦此種用法。

雖然優先推薦經過構造函數的方式注入來獲取服務,可是很難避免有些時候須要手工獲取服務,在使用手工獲取服務的時候,咱們應當從HttpContext.RequestServices中獲取。

使用第三方依賴注入框架

ASP.NET Core內置的依賴注入框架功能有限,當咱們想使用第三方框架的特性時,咱們能夠替換默認依賴注入框架。

ASP.NET Core內置的依賴注入框架未包含的特性:

  • 屬性注入
  • 基於名稱的注入
  • 子容器
  • 自定義生命週期管理
  • 對lazy對象初始化的Func 支持

若是要是用這些功能,咱們可使用第三方框架。本文采用官方文檔中的Autofac框架。

  • 首先添加 Autofac、Autofac.Extensions.DependencyInjection 的引用
  • 在Startup.ConfigureServices中配置容器,並返回IServiceProvider。在使用第三方容器時,必須返回IServiceProvider。
public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    // Add other framework services

    // Add Autofac
    var containerBuilder = new ContainerBuilder();
    containerBuilder.RegisterModule<DefaultModule>();
    containerBuilder.Populate(services);
    var container = containerBuilder.Build();
    return new AutofacServiceProvider(container);
}
  • 配置Autofac的Module,用來註冊服務等
public class DefaultModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<CharacterRepository>().As<ICharacterRepository>();
    }
}

參考資料

相關文章
相關標籤/搜索