依賴注入在 dotnet core 中實現與使用:2 使用 Extensions DependencyInjection

既然是依賴注入容器,必然會涉及到服務的註冊,獲取服務實例,管理做用域,服務注入這四個方面。html

  • 服務註冊涉及如何將咱們的定義的服務註冊到容器中。這一般是實際開發中使用容器的第一步,而容器自己一般是由框架來實例化的,大多數時候,並不須要本身初始化容器。
  • 獲取服務實例這一步,在實際開發中一般也不涉及,服務示例通常是經過注入來實現的。這裏只是爲了讓咱們對容器的使用瞭解的更全面一些。
  • 管理做用域通常在開發中也不涉及,框架,例如 .NET 的 MVC 框架已經幫咱們把這個問題處理了。
  • 服務注入是咱們須要關注的,不一樣的依賴注入容器支持不一樣的注入方式。在使用中,咱們會經過注入來獲取服務對象的實例。而不是本身 new 出來。

 看起來很複雜,使用的時候其實很簡單。git

1. 服務註冊

1.1 支持不一樣的做用域 Scope

DependencyInjection 經過 Add 方法來進行服務註冊,三種不一樣的做用域經過三種帶有不一樣後綴的 Add 方法來支持。
 
services.AddSingleton<IUserService, UserService>();
services.AddScoped<IUserService, UserService>();
services.AddTransient<IUserService, UserService>();

Singleton 就是單例,之後經過該容器獲取出來的,都是同一個服務對象實例。github

Scoped 就是限定了做用域,在每一個特定的做用域中,只會有一個服務對象實例。做用域須要你本身來建立,後面在使用服務的時候,咱們再介紹。編程

而 Transient 則在每次從容器中獲取的時候,都對建立新的服務對象實例。  框架

三種做用域簡單明瞭,後面咱們介紹服務註冊的時候,就再也不關注服務的做用域,都使用單例來介紹,其它兩種方式是相同的。ide

1.2 基於接口註冊

這是最爲常見的註冊方式,在實際開發中,服務通常都有對應的接口。爲了方便註冊,在 .NET Core 中通常使用泛型方式進行註冊,這樣比較簡潔。是最推薦的方式。函數

services.AddSingleton<IUserService, UserService>();

固然,也可使用基於類型的方式註冊,不過代碼沒有使用泛型方式優雅。你須要先獲取服務的類型,再經過類型進行註冊。ui

services.AddSingleton(typeof(IUserService), typeof(UserService));

1.3 直接註冊實例

若是服務並無對應的接口,能夠直接使用對象實例註冊,ServiceCollection 會直接使用該實例類型做爲服務接口類型來註冊,這種方式比較簡單粗暴。spa

services.AddSingleton(typeof(UserService));

 

1.4 註冊泛型服務

泛型是很強大的特性,例如,泛型的倉儲,咱們只須要一個泛型倉儲,就能夠經過它訪問針對不一樣類型的數據。.net

容器必需要支持泛型的服務,例如,針對下面的簡化倉儲示例,注意這裏的 Get() 簡化爲只返回默認值。

public interface IRepository<T>
{
    T Get(int id);
}

public class Repository<T>: IRepository<T> {
    public Repository() {
        Console.WriteLine(typeof(T).Name);
    }

    public T Get(int id){
        return default(T); 
    }
}

只須要註冊一次,就能夠得到不一樣實現的支持。可是,泛型比較特殊,不能這樣寫:

 services.AddSingleton<IRepository<>, Repository<>>();

你須要經過類型的方式來進行服務註冊。

services.AddSingleton(typeof(IRepository<>), typeof(Repository<>));  

若是沒有這個倉儲接口的話,就能夠這樣註冊。

services.AddSingleton(typeof(Repository<>));

若是涉及到多個類型的泛型,例如倉儲的定義變成下面這樣,id 的類型使用 K 來指定。

public interface IRepository<T, K>
{
    T Get(K id);
}

而倉儲的實現也變成下面這樣:

public class Repository<T, K>: IRepository<T, K> {
    public Repository() {
        Console.WriteLine(typeof(T).Name);
    }

    public T Get(K id){
        return default(T); 
    }
}

註冊的時候,就須要寫成下面的樣子:

services.AddSingleton (typeof (IRepository<,>), typeof (Repository<,>));
 

1.5 工廠模式註冊

除了讓容器幫助構造服務對象實例,咱們也能夠本身定義構建服務對象實例的工廠供容器使用。

若是服務實現沒有參數,例如 UserRepository,能夠這樣寫:

services.AddSingleton<IUserRepository>( (serviceProvider) => {
    return new UserReposotory();
    } );

若是構造函數是有參數的,好比:

public class UserReposotory: IUserRepository { 
    public UserReposotory(string name){
        Console.WriteLine( name);
    }
}  

註冊就變成了下面的樣子,須要注意的是,工廠會獲得一個 ServiceProvider 的實例供你使用。

services.AddSingleton<IUserRepository> ((serviceProvider) => {
            Console.WriteLine ("construction IUserReposority");
            return new UserReposotory ("Tom");
        });

1.6 直接使用 ServiceDescriptor 註冊

實際上,上面各類註冊方式最終都會經過建立一個服務描述對象來完成註冊,它的定義以下,你能夠看到上面各類註冊方式所涉及的痕跡。

public class ServiceDescriptor
{
    public ServiceLifetime Lifetime { get; }
    public Type ServiceType { get; }
    public Type ImplementationType { get; }
    public object ImplementationInstance { get; }
    public Func<IServiceProvider, object> ImplementationFactory { get; }
}

因此,實際上,你能夠經過本身建立這個服務描述對象來進行註冊。

services.Add(ServiceDescriptor.Singleton<IUserService, UserService>());

異曲同工,其實與使用上面的方式效果是同樣的。

2 管理 Scope

若是註冊服務的時候,指定了服務是單例的,不管從哪一個 Scope 中得到的服務實例,都會是同一個。因此,單例的服務與 Scope 就沒有什麼關係了。

若是註冊服務的時候,指定了服務是瞬態的,不管從哪一個 Scope 中獲取服務實例,都會是新建立的,每次獲取就新建立一個。因此,瞬態的服務與 Scope 也沒有什麼關係了。

只有在註冊時聲明是 Scoped 的服務,纔會涉及到 Scope。因此,只有 Scoped 的服務纔會涉及到 Scope。

在容器初始化以後,會建立一個根的 Scope 做用域,它是默認的。 

IServiceProvider provider = services.BuildServiceProvider ();

在須要做用域的時候,能夠經過已經建立的 provider 來建立做用域,而後從這個新建立的做用域再獲取當前的服務提供器。

IServiceProvider provider = services.BuildServiceProvider ();
IServiceScope scope = provider.CreateScope();

IServiceProvider scopedServiceProvider = scope.ServiceProvider;  

經過做用域的服務提供器獲取服務對象實例的時候,就會影響到經過 Scoped 註冊的服務了。

這種類型的服務在每個 Scope 中只會建立一個。

在微軟的實現中,不支持嵌套的 Scope。

3 注入服務

 如今又回到了涉及編程中的使用,在須要服務對象實例的時候,咱們只須要注入。

微軟的實現實際上只支持了構造函數注入。例如:

public class HomeController : Controller
{
    private readonly IDateTime _dateTime;

    public HomeController(IDateTime dateTime)
    {
        _dateTime = dateTime;
    }

 

4 常見問題

 屢次註冊問題

你能夠對同一個服務類型屢次註冊,這並不會遇到異常。在獲取服務對象實例的時候,是經過最後一次註冊來支持的。

須要的話,也能夠獲取到全部註冊的服務對象實例。

var instances = provider.GetServices(typeof(IUserService));  

不是直接注入服務,而是注入容器

容器自己也能夠注入,它的類型是 IServiceProvider,這樣,你能夠本身來經過容器完成特殊的處理。

var sp = provider.GetService<IServiceProvider> ();
var userService = sp.GetService<IUserService> ();
Console.WriteLine (userService);

  

5 簡化註冊過程

 https://www.cnblogs.com/catcher1994/p/handle-multi-implementations-with-same-interface-in-dotnet-core.html

 

參考資料:

相關文章
相關標籤/搜索