深入理解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请求参数验证 我个人比较懒,能自动做的事绝 ...
随机推荐
- Qt5学习(2)
1.学习了qt quick application 这是一种跟application不同的设计方式.主要就是靠“拖拖拽拽”,然后设置属性(颜色,大小),布局(margins等),然后要注意控件的从属关 ...
- IDEA 配置及常用快捷键
常用快捷键 1.Ctrl+Alt+T 把选择的代码放入 try{} 或者 if{} 里 2.Ctrl+O 重写方法提示 3.Alt+回车 导包提示 4.Alt+/ 代码提示(默认不是这个,需要参照后文 ...
- hadoop配置环境变量
hadoop安装包解压 tar -xvf hadoop-2.7.7.tar.gz 解压成功ll查看文件 配置环境变量 1. vi /home/wj/hadoop-2.7.7/etc/hadoop/h ...
- ABA问题怎么解:AtomicStampedReference和AtomicMarkableReference
本博客系列是学习并发编程过程中的记录总结.由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅. 并发编程系列博客传送门 并发编程的基石--CAS机制这篇文章中介绍到CAS机制有 ...
- 线段树 or 并查集 (多一个时间截点)
There is a company that has N employees(numbered from 1 to N),every employee in the company has a im ...
- 【5min+】传说中的孪生兄弟? Memory and Span
系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...
- Activiti接受任务(receiveTask)
Activiti接受任务(receiveTask) 作者:Jesai 前言: Activiti接受任务(receiveTask)其实和Activiti的手动任务是差不多的,不过手动任务是直接通过,而A ...
- Java.数据结构.集合体系详解
I. 第一部分:常见数据结构 首先简单说下数据结构. 什么是数据结构?数据结构就是组织数据的方式. 常见的数据结构:栈,堆,树,图,数组,队列,链表. 这里主要介绍与java集合体系相关的栈.数组和链 ...
- Element中(Notification)通知组件字体修改(Vue项目中Element的Notification修改字体)
这个问题纠结很久,一样的写的为啥有的页面就可以,有的就不行: 后来才发现: 先说一下怎么设置: 先定义customClass一个属性,用来写class属性值: 之后还需要修改一下组件里style标签的 ...
- Mac 查看端口占用情况及杀死进程
查看端口占用情况命令 sudo lsof -i :9000 冒号后面就是你需要查看的端口号. TheEternityZhang-MacBook:project zhtony$ sudo lsof -i ...