依赖注入在 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

 

参考资料:

相关文章
相关标签/搜索