浅谈ASP.NET Core中的DI

简介:

浅谈ASP.NET Core中的DI
什么是依赖注入(DI: Dependency Injection)?
依赖注入(DI)是一种面向对象的软件设计模式,主要是帮助开发人员开发出松耦合的应用程序。同时呢,让应用更容易进行单元测试和维护。

DI其实就是用一个注入器类为一个对象提供其依赖的一个过程!如何更好的理解呢?下面就举个列子解释下!

比如 class Client,它要使用服务class Service的提供的功能,这个时候就说Service是Client的依赖,程序实现如下:

var s = new Service();
var c = new Client(s);
很明显我们还要承担创建Service的对象的职责,程序出现了强耦合问题,后面如果需求变化,我们要替换掉Service,那我们就要修改这边的代码,这样的程序很面明,扩展性,灵活性比较差了!

引入DI之后呢,我们应该还有一个注入器类,假设是 class Injector 。为了更好的解释DI的好处,上面的代码我们重新设定为 class Client 依赖 接口IService , class Service 实现了IService ,这个时候我们的程序主流程不需要关注如何创建的Service,可以把这部分的职责委托给Injector,我们只要告诉Injector,我需要IService,请提供给我,程序实现如下:

var s = Injector.Get(typeof(IService));
var c = new Client(s);
这样的好处就很明显了,我们只关注自己的核心业务职责,对应依赖如何创建的,具体是什么类实现的,都不用自己管了,权力交给注入器就可以了!

 划重点:其实上面这个过程大家应该发现了我们把本来自己的一部分控制权,转交给了注入器去做,这个就是我们经常说的IOC(Inversion of Control,控制反转)。DI其实就是IOC计原则的一种实现。还有我们平常说的观察者默认,其实也是IOC的一种实现,核心就是把部分职责(非核心职责)转交出去,从而去构建出一种松耦合的应用!

什么是依赖注入容器(DI Container)?
DI Container ,也可以叫 IOC Container,其实是一个框架(Framework),它提供了一整套的DI解决方案,它负责创建依赖,然后自动把依赖注入到需要它们的其他对象里面,同时还负责管理依赖的生命周期!一些强大的第三方容器还提供各种各样的功能,使我们更加愉快的撸代码!

常用的第三方DI Container:

Spring.NET
 Autofac
Unity
Ninject
ASP .NET Core中的DI
在ASP.NET Core中,把依赖统一称作服务(services),所以DI Container就也被称为Service Container,Asp.NET Core提供了一个简单的内置容器 IServiceProvider ,它默认支持构造器注入 constructor injection,满足我们大多数的功能需求!

服务主要分为两类:

Framework Services:框架服务,由ASP.NET Core框架提供,例如 IApplicationBuilder 、IHostingEnvironment ... ,详情见https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1#framework-provided-services 。
Appliction Services:应用服务,由我们根据实际业务创建。
如果要通过DI容器自动实现注入我们的服务,我们必须要先在容器中登记服务(只需要注册应用服务,框架服务已经被ASP.NET Core框架注入了)。

注册服务(Registering Services)
假设我们有一个ILog接口和它的一个实现类,我们要把它注入到DI容器里面,然后在应用中使用它。

public interface ILog
{

void Info(string msg);
void Error(string err);

}

public class ConsoleLogger:ILog
{

public void Info(string msg)
{
    Console.WriteLine(msg);
}
public void Error(string err)
{
    Console.WriteLine(err);
}

}
然后在Startup类的ConfigureServices()方法中注册上面的服务,ConfigureServices()有一个IServiceCollection参数,就是用它来注册应用服务。

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {

    services.Add(new ServiceDescriptor(typeof(ILog), typeof(ConsoleLogger), ServiceLifetime.Singleton));            
} 

}
类ServiceDescriptor用来描述服务的类型、服务的具体实现已经服务的一个生命周期(Service Lifetime),上面我们指定了服务ILog的实现是ConsoleLogger,且是一个单例(Singleton)。

通常情况我们都是使用IServiceCollection扩展方法来注册服务

services.AddSingleton();//注册为单例

services.TryAddSingleton();
构造函数注入(Contractor Injection)
一旦我们注册了函数,当应用类的构造函数包含了需要依赖的服务,DI容器就自动帮我们注入依赖。

public class HomeController : Controller
{

ILog _log;

public HomeController(ILog log)
{
    _log = log;
}
public IActionResult Index()
{
    _log.Info("Executing /home/index");
    return View();
}

}    
控制器需要ILog服务,只需要在构造函数的参数中包含ILog类型即可,我们不需做其他任何事,DI容器自动给我们创建ILog的实例,并根据注入时指定的生命周期,在切当是时机销毁(Dispose)这个实例。

Action方法注入(Method Injection)
有时候,我们只需要在某一个方法中需要这个服务,这时,我们可以给方案的参数标记上[FromServices]    这个特性,容器就能自动为我们注入这个依赖服务的实例了

public IActionResult Index([FromServices] ILog log)
{

log.Info("Index method executing");
return View();

}
属性注入(Property Injection)
ASP.NET Core自带这个容器不支持,需要使用第三方容器,例如Autofac 。

服务的生命周期(Service Lifetime)
DI容器负责管理已注册服务的生命周期,它根据指定的生命周期自动销毁服务实例。ASP.NET Core 的服务可以配置以下三种生命周期形式:

Transient(瞬态)
每次从DI容器中解析(获取)服务时,DI容器都是返回一个新的服务实例。

//原始方法
services.Add(new ServiceDescriptor(typeof(ILog), typeof(ConsoleLogger), ServiceLifetime.Transient));
//扩展方法
services.AddTransient();
Scoped(作用域)
每一个作用域范围内(例如每一个HTTP 请求)从DI容器中解析出来的实例都是同一个

//原始方法
services.Add(new ServiceDescriptor(typeof(ILog), typeof(ConsoleLogger), ServiceLifetime.Scoped));
//扩展方法
services.AddScoped();
Singleton(单例)
只在第一次请求是创建,之后所有请求都共享同一个服务实例,直到应用程序的生命周结束。所以在ASP.NET Core应用中,没有必须手动去创建一个单例,通过注册一个单例服务到容器中即可。

//原始方法
services.Add(new ServiceDescriptor(typeof(ILog), new ConsoleLogger()));
//扩展方法
services.AddSingleton();
DI使用经验的一些总结
泛型如何注册?
通过开放式泛型(Open Generics)注册服务。假设上面的类型修改成 ILog 和

ConsoleLogger ,那么我们按照下面的方式注册即可

services.AddScoped(typeof(ILog<>), typeof(ConsoleLogger<>));
相同的接口类型注入两个实现类型会怎样?
如果我们在注册服务时,相同类型注册多次并不会报错,但在解析时,返回的是最后一次注册的类型的实例。

public interface ILog

{
    void Info(string msg);
}

public class Logger1 : ILog
{
    public void Info(string msg)
    {
    }
}

public class Logger2 : ILog
{
    public void Info(string msg)
    {
    }
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {

     services.AddTransient<ILog, Logger1>();
     services.AddTransient<ILog, Logger2>();

    }
}
public class HomeController : Controller
{
    ILog _log;
    public HomeController(ILog log)
    {

     _log = log;//_log is Logger2

     }
}
为了避免我们多次注册,导致具体实现被覆盖的问题,所以我们一般都是使用    TryAddTransient,这个方法注册时,检测到相同类型已经被注册过,就不会在进行注册

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {

     services.TryAddTransient<ILog, Logger1>();
     services.TryAddTransient<ILog, Logger2>();

    }
}
public class HomeController : Controller
{
    ILog _log;
    public HomeController(ILog log)
    {

     _log = log;//_log is Logger1

     }
}
自己想要手动从容器中获取服务对象,怎么做?
ASP.NET Core中的DI Container是IServiceProvider,只要获取这个对象,然后调用 GetService 这个方法即可。

如果在Controller中
HttpContext的属性RequestServices就是IServiceProvider类型,所以我们可以按照下面的方法:

  public IActionResult Index()
  {
      var services = this.HttpContext.RequestServices;
      var log = (ILog)services.GetService(typeof(ILog));

      log.Info("Index method executing");

      return View();
  }

如果在应用中
在应用中时,我们可以通过构造函数注入IServiceProvider

public class MyAppService
{

private IServiceProvider _services;
public MyAppService(IServiceProvider services)
{
    _services=services;
}
public void Test()
{
    //原始方法
    var log = (ILog)_services.GetService(typeof(ILog));
    //通过扩展方法,需要nuget添加Microsoft.Extensions.DependencyInjection.Abstractions 这个引用
    var log = _services.GetService<ILog>();
}

}
结语
ASP.NET Core已经很强大了,提供了很多实用功能,希望.Net的战友们能在基本知识储备的前提下,多多发掘总结出ASP.NET Core开发的最佳实践,为推动.NET Core生态建设贡献一份自己的力量!💪

作者:小伟06

出处:https://www.cnblogs.com/liuww/p/12540309.html

相关文章
|
17天前
|
数据可视化 网络协议 C#
C#/.NET/.NET Core优秀项目和框架2024年3月简报
公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:追逐时光者,第一时间获取每周精选分享资讯🔔)。
|
3月前
|
开发框架 前端开发 JavaScript
盘点72个ASP.NET Core源码Net爱好者不容错过
盘点72个ASP.NET Core源码Net爱好者不容错过
71 0
|
3月前
|
开发框架 .NET
ASP.NET Core NET7 增加session的方法
ASP.NET Core NET7 增加session的方法
37 0
|
3月前
|
开发框架 JavaScript .NET
ASP.NET Core的超级大BUG
ASP.NET Core的超级大BUG
42 0
|
1月前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
29 0
|
1月前
|
开发框架 人工智能 .NET
C#/.NET/.NET Core拾遗补漏合集(持续更新)
C#/.NET/.NET Core拾遗补漏合集(持续更新)
|
1月前
|
开发框架 中间件 .NET
C# .NET面试系列七:ASP.NET Core
## 第一部分:ASP.NET Core #### 1. 如何在 controller 中注入 service? 在.NET中,在ASP.NET Core应用程序中的Controller中注入服务通常使用<u>依赖注入(Dependency Injection)</u>来实现。以下是一些步骤,说明如何在Controller中注入服务: 1、创建服务 首先,确保你已经在应用程序中注册了服务。这通常在Startup.cs文件的ConfigureServices方法中完成。例如: ```c# services.AddScoped<IMyService, MyService>(); //
64 0
|
2月前
|
开发框架 前端开发 .NET
福利来袭,.NET Core开发5大案例,30w字PDF文档大放送!!!
为了便于大家查找,特将之前开发的.Net Core相关的五大案例整理成文,共计440页,32w字,免费提供给大家,文章底部有PDF下载链接。
35 1
福利来袭,.NET Core开发5大案例,30w字PDF文档大放送!!!
|
2月前
|
算法 BI API
C#/.NET/.NET Core优秀项目和框架2024年1月简报
C#/.NET/.NET Core优秀项目和框架2024年1月简报
|
3月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
42 0