既然是依赖注入容器,必然会涉及到服务的注册,获取服务实例,管理作用域,服务注入这四个方面。

  • 服务注册涉及如何将我们的定义的服务注册到容器中。这通常是实际开发中使用容器的第一步,而容器本身通常是由框架来实例化的,大多数时候,并不需要自己初始化容器。

  • 获取服务实例这一步,在实际开发中通常也不涉及,服务示例一般是通过注入来实现的。这里只是为了让我们对容器的使用了解的更全面一些。

  • 管理作用域一般在开发中也不涉及,框架,例如 .NET 的 MVC 框架已经帮我们把这个问题处理了。

  • 服务注入是我们需要关注的,不同的依赖注入容器支持不同的注入方式。在使用中,我们会通过注入来获取服务对象的实例。而不是自己 new 出来。

看起来很复杂,使用的时候其实很简单。

1. 服务注册

1.1 支持不同的作用域 Scope

DependencyInjection 通过 Add 方法来进行服务注册,三种不同的作用域通过三种带有不同后缀的 Add 方法来支持。

services.AddSingleton<IUserService, UserService>();
services.AddScoped<IUserService, UserService>();
services.AddTransient<IUserService, UserService>();

Singleton 就是单例,以后通过该容器获取出来的,都是同一个服务对象实例。

Scoped 就是限定了作用域,在每个特定的作用域中,只会有一个服务对象实例。作用域需要你自己来创建,后面在使用服务的时候,我们再介绍。

而 Transient 则在每次从容器中获取的时候,都对创建新的服务对象实例。  

三种作用域简单明了,后面我们介绍服务注册的时候,就不再关注服务的作用域,都使用单例来介绍,其它两种方式是相同的。

1.2 基于接口注册

这是最为常见的注册方式,在实际开发中,服务一般都有对应的接口。为了方便注册,在 .NET Core 中一般使用泛型方式进行注册,这样比较简洁。是最推荐的方式。

1
services.AddSingleton<IUserService, UserService>();

当然,也可以使用基于类型的方式注册,不过代码没有使用泛型方式优雅。你需要先获取服务的类型,再通过类型进行注册。

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

1.3 直接注册实例

如果服务并没有对应的接口,可以直接使用对象实例注册,ServiceCollection 会直接使用该实例类型作为服务接口类型来注册,这种方式比较简单粗暴。

1
services.AddSingleton(typeof(UserService));

1.4 注册泛型服务

泛型是很强大的特性,例如,泛型的仓储,我们只需要一个泛型仓储,就可以通过它访问针对不同类型的数据。

容器必须要支持泛型的服务,例如,针对下面的简化仓储示例,注意这里的 Get() 简化为只返回默认值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
    }
}

只需要注册一次,就可以获得不同实现的支持。但是,泛型比较特殊,不能这样写:

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

你需要通过类型的方式来进行服务注册。

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

如果没有这个仓储接口的话,就可以这样注册。

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

如果涉及到多个类型的泛型,例如仓储的定义变成下面这样,id 的类型使用 K 来指定。

1
2
3
4
public interface IRepository<T, K>
{
    T Get(K id);
}

而仓储的实现也变成下面这样:

1
2
3
4
5
6
7
8
9
public class Repository<T, K>: IRepository<T, K> {
    public Repository() {
        Console.WriteLine(typeof(T).Name);
    }
    public T Get(K id){
        return default(T);
    }
}

注册的时候,就需要写成下面的样子:

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

1.5 工厂模式注册

除了让容器帮助构造服务对象实例,我们也可以自己定义构建服务对象实例的工厂供容器使用。

如果服务实现没有参数,例如 UserRepository,可以这样写:

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

如果构造函数是有参数的,比如:

1
2
3
4
5
public class UserReposotory: IUserRepository {
    public UserReposotory(string name){
        Console.WriteLine( name);
    }
}  

注册就变成了下面的样子,需要注意的是,工厂会得到一个 ServiceProvider 的实例供你使用。

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

1.6 直接使用 ServiceDescriptor 注册

实际上,上面各种注册方式最终都会通过创建一个服务描述对象来完成注册,它的定义如下,你可以看到上面各种注册方式所涉及的痕迹。

1
2
3
4
5
6
7
8
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; }
}

所以,实际上,你可以通过自己创建这个服务描述对象来进行注册。

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

殊途同归,其实与使用上面的方式效果是一样的。

2 管理 Scope

如果注册服务的时候,指定了服务是单例的,无论从哪个 Scope 中获得的服务实例,都会是同一个。所以,单例的服务与 Scope 就没有什么关系了。

如果注册服务的时候,指定了服务是瞬态的,无论从哪个 Scope 中获取服务实例,都会是新创建的,每次获取就新创建一个。所以,瞬态的服务与 Scope 也没有什么关系了。

只有在注册时声明是 Scoped 的服务,才会涉及到 Scope。所以,只有 Scoped 的服务才会涉及到 Scope。

在容器初始化之后,会创建一个根的 Scope 作用域,它是默认的。

1
IServiceProvider provider = services.BuildServiceProvider ();

在需要作用域的时候,可以通过已经创建的 provider 来创建作用域,然后从这个新创建的作用域再获取当前的服务提供器。

1
2
3
4
IServiceProvider provider = services.BuildServiceProvider ();
IServiceScope scope = provider.CreateScope();
IServiceProvider scopedServiceProvider = scope.ServiceProvider;  

通过作用域的服务提供器获取服务对象实例的时候,就会影响到通过 Scoped 注册的服务了。

这种类型的服务在每一个 Scope 中只会创建一个。

在微软的实现中,不支持嵌套的 Scope。

3 注入服务

现在又回到了涉及编程中的使用,在需要服务对象实例的时候,我们只需要注入。

微软的实现实际上只支持了构造函数注入。例如:

1
2
3
4
5
6
7
8
public class HomeController : Controller
{
    private readonly IDateTime _dateTime;
    public HomeController(IDateTime dateTime)
    {
        _dateTime = dateTime;
    }

 

4 常见问题

多次注册问题

你可以对同一个服务类型多次注册,这并不会遇到异常。在获取服务对象实例的时候,是通过最后一次注册来支持的。

需要的话,也可以获取到所有注册的服务对象实例。

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

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

容器本身也可以注入,它的类型是 IServiceProvider,这样,你可以自己来通过容器完成特殊的处理。

1
2
3
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

参考资料:

  • ServiceCollectionServiceExtensions at GitHub

  • Does .net core dependency injection support Lazy<T>

依赖注入在 dotnet core 中实现与使用:2 使用 Extensions DependencyInjection相关推荐

  1. 依赖注入在 dotnet core 中实现与使用:1 基本概念

    关于 Microsoft Extension: DependencyInjection 的介绍已经很多,但是多数偏重于实现原理和一些特定的实现场景.作为 dotnet core 的核心基石,这里准备全 ...

  2. dotNET Core 中怎样操作AD(续1)

    在之前的文章<dotNET Core 中怎样操作 AD?>中主要以AD的数据同步到数据库的场景来描述了在 dotNetCore 中怎样操作AD,本文将继续介绍一些在 dotNetCore ...

  3. dotNET Core 中怎样操作 AD?

    做企业应用开发难免会跟 AD 打交道,在之前的 dotNET FrameWork 时代,通常使用 System.DirectoryServices 的相关类来操作 AD ,在 dotNET Core ...

  4. winform防止sqlserver注入_ASP.NET Core 中的 依赖注入介绍

    ASP.NET Core 依赖注入 HomeController public class HomeController : Controller { private IStudentReposito ...

  5. 依赖注入底层反射原理_Spring中反射与反射的原理(转载)

    Spring 中的反射与反射的原理 | Depp Wang's Blog 在造轮子:实现一个简易的 Spring IoC 容器一文中提到 Spring 在创建 Bean 实例和依赖注入时使用了反射,本 ...

  6. 浅谈_依赖注入 asp.net core

    1.1什么是依赖 我们先看下图 可以简单理解,一个HomeController类使用到了DBContext类,而这种关系是有偶然性,临时性,弱关系的,但是DBContext的变化会影响到HomeCon ...

  7. [转载]ASP.NET Core 源码阅读笔记(1) ---Microsoft.Extensions.DependencyInjection

    这篇随笔主要记录一下ASP.NET Core团队实现默认的依赖注入容器的过程,我的理解可能并不是正确的. DependencyInjection这个项目不大,但却是整个ASP.NET Core的基础, ...

  8. ASP.NET Core 源码阅读笔记(1) ---Microsoft.Extensions.DependencyInjection

    这篇随笔主要记录一下ASP.NET Core团队实现默认的依赖注入容器的过程,我的理解可能并不是正确的. DependencyInjection这个项目不大,但却是整个ASP.NET Core的基础, ...

  9. 如何在.NET Core控制台程序中使用依赖注入

    背景介绍 依赖注入(Dependency Injection), 是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度.在.NET Core MVC中 我们可以在Startup.cs文件的Co ...

最新文章

  1. 龙芯服务器cpu支持最大数量,龙芯启动全国六个适配中心:加速生态建设
  2. 微博平台StatusNet研究(3):友好URL与OpenID支持
  3. 腾讯AI×王者荣耀「绝悟」项目首亮相:KPL秋季决赛击败顶尖战队
  4. 【网易MCTalk】除了低价促销、丰富品类,电商就没有新玩法了?
  5. switch日版有中文吗_任天堂switch国行和日版的区别
  6. python中浮点型和十进制_Python3.2官方文件翻译-工具列表和十进制浮点计算
  7. 两平面平行但不重合的条件是_____「初一数学」平行线的判定与性质的综合应用...
  8. 宝塔pure-ftpd文件管理不显示目录|文件夹打不开
  9. ThreadPoolExecutor使用和思考-线程池大小设置与BlockingQueue的三种
  10. Myeclipse错误:Errors occurred during the build. 解决方法
  11. 中心滤波与均值滤波MATLAB
  12. 贴片工字电感的封装及结构
  13. 什么是软件质量——基于ISO质量定义对软件质量的理解
  14. LabVIEW编程LabVIEW开发 旧程序升级维护
  15. 游戏合作伙伴专题:BreederDAO 与 Mech 一起加入战斗
  16. 机器视觉之缺陷检测的光源
  17. 计算机考试感受作文,关于考试感受作文5篇
  18. LQ0076 既约分数【GCD】
  19. videoJs实现手机视频播放功能
  20. 我用Python写了一个PDF转换器!

热门文章

  1. 将组策略编辑器添加到控制面板
  2. Crash 的文明世界
  3. zookeeper学习03 使用场景
  4. SQL Server Update 所有表的某一列(列名相同,类型相同)数值
  5. JS多个对象添加到一个对象中
  6. C# 操作FireBird 附源码
  7. 使用bat来运行cygwin,执行脚本(命令)
  8. group client policy无法登录,谢绝访问
  9. C++程序运行时内存布局之--无继承情况下的虚函数
  10. .net 垃圾回收机制