深入理解ASP.NET Core依赖注入

概述
ASP.NET Core可以说是处处皆注入,本文从基础角度理解一下原生DI容器,及介绍下怎么使用并且如何替换官方提供的默认依赖注入容器。
什么是依赖注入
百度百科中对于依赖注入的定义:控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
依赖反转前
那么在依赖反转之前或者叫控制反转之前,直接依赖是怎么工作的呢,这里ClassA直接依赖ClassB,而ClassB又直接依赖ClassC,任何一处的变动都会牵一发而动全身,不符合软件工程的设计原则。
依赖反转后
应用依赖关系反转原则后,A 可以调用 B 实现的抽象上的方法,让 A 可以在运行时调用 B,而 B 又在编译时依赖于 A 控制的接口(因此,典型的编译时依赖项发生反转) 。 运行时,程序执行的流程保持不变,但接口引入意味着可以轻松插入这些接口的不同实现。
依赖项反转是生成松散耦合应用程序的关键一环,因为可以将实现详细信息编写为依赖并实现更高级别的抽象,而不是相反 。 因此,生成的应用程序的可测试性、模块化程度以及可维护性更高。 遵循依赖关系反转原则可实现依赖关系注入 。
何谓容器
如果你用过Spring,就知道其庞大而全能的生态圈正是因为有了它包含各种各样的容器来做各种事情,其本质也是一个依赖反转工厂,那么不知道你注意到没有,控制反转后产生依赖注入,这样的工作我们可以手动来做,那么如果注入的服务成千上万呢,那怎么玩呢?那么问题来了,控制反转了,依赖的关系也交给了外部,现在的问题就是依赖太多,我们需要有一个地方来管理所有的依赖,这就是容器的角色。
容器的主要职责有两个:绑定服务与实例之间的关系(控制生命周期)。获取实例并对实例进行管理(创建和销毁)。
ASP.NET Core里依赖注入是怎么实现的
在.Net Core里提供了默认的依赖注入容器IServiceCollection,它是一个轻量级容器。核心组件为两个IServiceCollection和IServiceProvider,IServiceCollection负责注册,IServiceProvider负责提供实例。
使用两个核心组件前导入命名空间Microsoft.Extensions.DependencyInjection.
默认的ServiceCollection有以下三个方法:
IServiceCollection serviceCollection=new ServiceCollection();
#三个方法都是注册实例,只不过实例的生命周期不一样。
#单例模式,只有一个实例
serviceCollection.AddSingleton<ILoginService, EFLoginService>();
#每次请求都是同一个实例,比如EntityFramework.Context
serviceCollection.AddScoped<ILoginService, EFLoginService>();
#每次调用都是不同的实例
serviceCollection.AddTransient<ILoginService, EFLoginService>();
#接口声明
public interface IServiceCollection : IList<ServiceDescriptor>, ICollection<ServiceDescriptor>, IEnumerable<ServiceDescriptor>, IEnumerable
{
}
#默认的ServiceCollection实际上是一个提供了ServiceDescriptor的List。
public class ServiceCollection : IServiceCollection, ICollection<ServiceDescriptor>, IEnumerable<ServiceDescriptor>, IEnumerable, IList<ServiceDescriptor>
{
private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
public int Count
{
get
{
return this._descriptors.Count;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
public ServiceDescriptor this[int index]
{
get
{
return this._descriptors[index];
}
set
{
this._descriptors[index] = value;
}
}
public void Clear()
{
this._descriptors.Clear();
}
public bool Contains(ServiceDescriptor item)
{
return this._descriptors.Contains(item);
}
public void CopyTo(ServiceDescriptor[] array, int arrayIndex)
{
this._descriptors.CopyTo(array, arrayIndex);
}
public bool Remove(ServiceDescriptor item)
{
return this._descriptors.Remove(item);
}
public IEnumerator<ServiceDescriptor> GetEnumerator()
{
return (IEnumerator<ServiceDescriptor>) this._descriptors.GetEnumerator();
}
void ICollection<ServiceDescriptor>.Add(ServiceDescriptor item)
{
this._descriptors.Add(item);
}
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator) this.GetEnumerator();
}
public int IndexOf(ServiceDescriptor item)
{
return this._descriptors.IndexOf(item);
}
public void Insert(int index, ServiceDescriptor item)
{
this._descriptors.Insert(index, item);
}
public void RemoveAt(int index)
{
this._descriptors.RemoveAt(index);
}
}
三个方法对应的生命周期值,在枚举ServiceLifeTime中定义:
public enum ServiceLifetime
{
Singleton,
Scoped,
Transient,
}
三个方法确切来说是定义在扩展方法ServiceCollectionServiceExtensions中定义:
#这里我列出个别方法,详细可参看源码
#导入Microsoft.Extensions.DependencyInjection
public static class ServiceCollectionServiceExtensions
{
public static IServiceCollection AddTransient(
this IServiceCollection services,
Type serviceType,
Type implementationType)
{
if (services == null)
throw new ArgumentNullException(nameof (services));
if (serviceType == (Type) null)
throw new ArgumentNullException(nameof (serviceType));
if (implementationType == (Type) null)
throw new ArgumentNullException(nameof (implementationType));
//这里注入时指定ServiceLifetime.Transient
return ServiceCollectionServiceExtensions.Add(services, serviceType, implementationType, ServiceLifetime.Transient);
}
public static IServiceCollection AddScoped(
this IServiceCollection services,
Type serviceType,
Type implementationType)
{
if (services == null)
throw new ArgumentNullException(nameof (services));
if (serviceType == (Type) null)
throw new ArgumentNullException(nameof (serviceType));
if (implementationType == (Type) null)
throw new ArgumentNullException(nameof (implementationType));
//这里注入时指定ServiceLifetime.Scoped
return ServiceCollectionServiceExtensions.Add(services, serviceType, implementationType, ServiceLifetime.Scoped);
}
public static IServiceCollection AddSingleton(
this IServiceCollection services,
Type serviceType,
Type implementationType)
{
if (services == null)
throw new ArgumentNullException(nameof (services));
if (serviceType == (Type) null)
throw new ArgumentNullException(nameof (serviceType));
if (implementationType == (Type) null)
throw new ArgumentNullException(nameof (implementationType));
//这里注入时指定ServiceLifetime.Singleton
return ServiceCollectionServiceExtensions.Add(services, serviceType, implementationType, ServiceLifetime.Singleton);
}
}
ASP.NET Core里依赖注入是怎样运行的
在Startup中初始化
ASP.NET Core在Startup.ConfigureService中注入指定服务,可以从方法参数IServiceCollection中看出,这里还有个方法services.AddMvc(), 这个MVC框架本身自己注入的服务,定义在MvcServiceCollectionExtesnsions类中。
#Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<ILoginService, EFLoginService>();
}
在构造函数中注入
官方推荐在构造器中注入,这里也是为了体现显示依赖。
public class AccountController
{
private ILoginService _loginService;
public AccountController(ILoginService loginService)
{
_loginService = loginService;
}
}
如何替换其他容器
前面提到原生的依赖注入容器只是一个轻量级容器,但是功能真的很有限,比如属性注入、方法注入、子容器、lazy对象初始化支持。为何不好好借鉴一下Spring强大的背景呢,所以这里我们用Autofac替换系统默认的依赖注入容器。先引用命名空间Autofac、Autofac.Extensions.DependencyInjection。我本机环境使用的.Net Core3.0。 3.0不能修改直接修改Startup的ConfigureService方法了,直接修改ConfigureService方法返回值会抛出异常ConfigureServices returning an System.IServiceProvider isn't supported. 这里可以参考Autofac文档,已经有说明。
修改Startup
#直接声明方法ConfigureContainer
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddMvc();
}
public void ConfigureContainer(ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<EFLoginService>().As<ILoginService>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
修改Program
#导入命名空间Autofac.Extensions.DependencyInjections,然后调用UseServiceProviderFactory
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(
webBuilder => { webBuilder.UseStartup<Startup>(); })
.UseServiceProviderFactory(new AutofacServiceProviderFactory());
}
参考链接
https://docs.microsoft.com/zh-cn/dotnet/architecture/modern-web-apps-azure/architectural-principles#dependency-inversion
https://www.cnblogs.com/loogn/p/10566510.html
https://www.cnblogs.com/jesse2013/p/di-in-aspnetcore.html
深入理解ASP.NET Core依赖注入的更多相关文章
- 全面理解 ASP.NET Core 依赖注入
DI在.NET Core里面被提到了一个非常重要的位置, 这篇文章主要再给大家普及一下关于依赖注入的概念,身边有工作六七年的同事还个东西搞不清楚.另外再介绍一下.NET Core的DI实现以及对实例 ...
- 全面理解 ASP.NET Core 依赖注入 (转载)
DI在.NET Core里面被提到了一个非常重要的位置, 这篇文章主要再给大家普及一下关于依赖注入的概念,身边有工作六七年的同事还个东西搞不清楚.另外再介绍一下.NET Core的DI实现以及对实例 ...
- 理解ASP.NET Core 依赖注入
目录: 一.什么是依赖注入 1.1.什么是依赖? 1.2. 什么是注入? 1.3.依赖注入解决的问题 二.服务的生命周期(.Net Core DI) 三.替换默认服务容器 3.1.为什么替换默认服务容 ...
- asp.net core 依赖注入几种常见情况
先读一篇注入入门 全面理解 ASP.NET Core 依赖注入, 学习一下基本使用 然后学习一招, 不使用接口规范, 直接写功能类, 一般情况下可以用来做单例. 参考https://www.cnblo ...
- [译]ASP.NET Core依赖注入深入讨论
原文链接:ASP.NET Core Dependency Injection Deep Dive - Joonas W's blog 这篇文章我们来深入探讨ASP.NET Core.MVC Core中 ...
- ASP.NET Core依赖注入——依赖注入最佳实践
在这篇文章中,我们将深入研究.NET Core和ASP.NET Core MVC中的依赖注入,将介绍几乎所有可能的选项,依赖注入是ASP.Net Core的核心,我将分享在ASP.Net Core应用 ...
- # ASP.NET Core依赖注入解读&使用Autofac替代实现
标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Aut ...
- 实现BUG自动检测 - ASP.NET Core依赖注入
我个人比较懒,能自动做的事绝不手动做,最近在用ASP.NET Core写一个项目,过程中会积累一些方便的工具类或框架,分享出来欢迎大家点评. 如果以后有时间的话,我打算写一个系列的[实现BUG自动检测 ...
- 自动化CodeReview - ASP.NET Core依赖注入
自动化CodeReview系列目录 自动化CodeReview - ASP.NET Core依赖注入 自动化CodeReview - ASP.NET Core请求参数验证 我个人比较懒,能自动做的事绝 ...
随机推荐
- BigInteger&BigDecimal类
BigInteger类 当需要处理超过 long 数值范围的大整数时,java.math 包中的 BigInteger 类提供任意精度的整数运算. 构造方式 //构造方法,将BigInteger的十进 ...
- static和final关键字
static关键字 静态变量 静态变量:又称做类变量,也就是这个变量属于整个类,而不属于单个实例.类所有的实例共享静态变量,可以直接通过类名来访问它.静态变量在内存中只存在一份,当系统第一次加载类时, ...
- MySQL之性能优化
查看执行计划explain 1.1 Explain命令:它可以对select语句进行分析,并输出select执行的详细信息,以对开发人员针对性优化 1.2 Explain的用法:在selec ...
- 用Python爬取了考研吧1000条帖子,原来他们都在讨论这些!
写在前面 考研在即,想多了解考研er的想法,就是去找学长学姐或者去网上搜索,贴吧就是一个好地方.而借助强大的工具可以快速从网络鱼龙混杂的信息中得到有价值的信息.虽然网上有很多爬取百度贴吧的教程和例子, ...
- 如何在ArcGIS中恢复MapGIS制图表达信息
1.输出符号信息 Map2Shp软件中提供了图示表达转换功能,提供对MapGIS图形特征可视表达信息的跨平台支持.若要使用该功能,必须在转换时,"图元参数输出方式"选定为[图元参数 ...
- Spring Boot2 系列教程 (六) | 使用 JdbcTemplates 访问 Mysql
前言 如题,今天介绍 springboot 通过jdbc访问关系型mysql,通过 spring 的 JdbcTemplate 去访问. 准备工作 SpringBoot 2.x jdk 1.8 mav ...
- 跟着知识追寻者学BeautifulSoup,你学不会打不还口,骂不还手
一 前言 Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库:其强大的提取能力让知识追寻者放弃了使用正则匹配查找HTML节点:Beautifu Soup 其能直接 ...
- Go 每日一库之 cobra
简介 cobra是一个命令行程序库,可以用来编写命令行程序.同时,它也提供了一个脚手架, 用于生成基于 cobra 的应用程序框架.非常多知名的开源项目使用了 cobra 库构建命令行,如Kubern ...
- NTT 求原根
使用NTT需要保证模数mod 为质数. 通过以下代码求得一个模数的原根 , 常见的质数的原根 998244353 -> 3 1e9+7 -> 5 #include<bits/ ...
- 设置java启动项目
1,