深入理解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.Transientreturn 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.Scopedreturn 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.Singletonreturn 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/311712.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

word List 34

word List 34 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

redis api 中文文档_我的Redis学习资料库

最近经常有人问我&#xff0c;我这边学习Redis都有哪些资料&#xff0c;能不能发他们一下。作为一个习惯”慢慢啃”技术学习的人&#xff0c;我确实积累了大量资料&#xff0c;这里可以介绍给大家。注明&#xff1a;本文提供的书籍、视频、源码、文档等&#xff0c;都是非盈利性…

前后端分离架构一直没机会实战?1周完成Vue+Core WebApi移动商城实战(含源码)!...

疫情让企业受到重创&#xff01;就业形势更加严峻&#xff01;前后端分离架构成了当下最高频的招聘需求还没实战过前后端分离&#xff1f;花3分钟阅读本文&#xff0c;带你全面了解前后端分离&#xff0c;轻松面试拿高薪&#xff01;Web发展至今技术非常成熟&#xff0c;主流有…

算法---宝藏最优选择问题

贪心算法—宝藏最优选择问题 原理参考趣学算法 代码&#xff1a; #include"quickSort1.h" 快速排序代码 int quickSort1(int a[], int l, int h) {//快速排序int i l, j h, p a[l];while (i < j) {while (i<j&&a[j]>p) {//从右往左遍历查找比…

C# 的未来:主构造函数

我们上次提到主构造函数是 2014 年&#xff0c;当时&#xff0c; C# 6 和 VB 12 的候选列表上去掉了主构造函数。去年底&#xff0c;主构造函数重新出现&#xff0c;作为提案#2691 &#xff0c;现在成为 C# 9 的一个候选项。主构造函数背后的基本思想是减少初始化类所需的样板代…

python unpack_ip地址处理每天10行python代码系列

背景知识:ip地址(这里都是指的ipv4地址)是一个32位(4字节)的无符号整数。为方便记忆&#xff0c;把每个字节看做一个无符号数&#xff0c;并以.号隔开。比如常见的私有地址192.168.100.1, dns 114.114.114.114。下面这段代码&#xff0c;展示python如何处理ip地址的一些方法。i…

word List35

word List35 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

exceptionhandler注解_SpringMVC 中 @ControllerAdvice 注解的三种使用场景!

ControllerAdvice &#xff0c;很多初学者可能都没有听说过这个注解&#xff0c;实际上&#xff0c;这是一个非常有用的注解&#xff0c;顾名思义&#xff0c;这是一个增强的 Controller。使用这个 Controller &#xff0c;可以实现三个方面的功能&#xff1a;全局异常处理全局…

在家做科研是一种什么样的体验?

匿名提问&#xff1a;题主是 MSRA 的一名实习生&#xff0c;大四。在家咸鱼了半个月&#xff0c;这周打算督促自己继续科研&#xff01;&#xff08;再不火力全开对不起公司不停发的工资和房补&#xff09;。可是在家里似乎很难静下心读 paper&#xff0c;工作五分钟就可以奖励…

word List36

word List36 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

C++实现链式存储二叉树

#include <iostream> using namespace std; typedef char ElemType;typedef struct BiNode {//节点的创建ElemType data;struct BiNode *lchild, *rchild; } BiNode, *BiTree;void PreOrderTraverse(BiTree T) {//先序遍历if (T) {cout << T->data;PreOrderTrav…

37岁跳槽去阿里 p8_36岁阿里p8发帖:手下12个p7,17个p6,亲自写代码毫无中年危机...

关注微信公众号【程序员干货站】&#xff0c;可加入技术交流群~程序员热议的“35 岁中年危机”&#xff0c;其实很多人都没搞懂本质上是因为什么而焦虑。最近看脉脉上有阿里员工发帖称&#xff1a;“今年36岁&#xff0c;p8&#xff0c;手下29个人&#xff0c;12个p7,17个p6&am…

数据结构---判断一棵树是否是二叉搜索树

数据结构—判断一棵树是否是二叉搜索树 代码&#xff1a; #pragma once #define N 100 #define elemType BTree* #include<stdlib.h> typedef struct BTree {int data;struct BTree *lchild, *rchild; }BTree; typedef struct dQueue {elemType data;struct dQueue* ne…

【要闻】Kubernetes安全问题严峻、Linux v5.4安全性浅谈

以下为您奉上今天的开源领域要闻。谷歌提前发布Android 11首个开发者预览版谷歌通常会在三月推出即将发布的Android预览版本。但谷歌今年更早实现了该功能&#xff0c;现已发布了Android 11首个开发者预览版&#xff0c;目前仅支持部分Pixel手机。今年的重点是开发人员部分&…

旧键盘

题目背景 小明的键盘使用年限长&#xff0c;输入时键盘的按键时不时会有粘滞。出现粘滞情况时&#xff0c;上一个被按下的按键会被长按&#xff0c;而字符可能被输入一次或多次。 题目描述 给出小明将要输入的字符串s&#xff0c;和小明实际输入的字符串t&#xff0c;请你判断…

mac vim python3_VIM学习笔记 编译源码(Compile Code)-Python

Python作为一种解释型编程语言&#xff0c;需要解释器来编译并执行Python代码。测试Python对于Linux和Mac操作系统&#xff0c;均已预装Python。而在Windows下&#xff0c;可以使用安装包或者直接解压版zip文件。使用以下命令&#xff0c;可以查看当前Python版本&#xff1a;$ …

word List37

word List37 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

EntityFramework Core 3.x初级篇发布

之前陆陆续续有一些童鞋一直催我什么时候出EF Core视频&#xff0c;因个人时间确实有限&#xff0c;中间也一直有想法去录制&#xff0c;但是还是因为各种原因而难产&#xff0c;刚好最近因为疫情的事情&#xff0c;个人时间比较充裕&#xff0c;于是下定决心开始准备录制EF Co…

对pca降维后的手写体数字图片数据分类_机器学习:数据的准备和探索——特征提取和降维...

在数据的预处理阶段&#xff0c;特征提取和数据降维是提升模型表示能力的一种重要手段。特征提取主要是从数据中找到有用的特征&#xff0c;用于提升模型的表示能力&#xff0c;而数据降维主要是在不减少模型准确率的情况下减少数据的特征数量。比如&#xff0c;我们要搭建一个…

程序员应对浏览器同源策略的姿势

同源策略浏览器最基本的安全规范——同源策略(Same-Origin Policy)。所谓同源是指域名、协议、端口相同。不同源的浏览器脚本(javascript、ActionScript、canvas)在没明确授权的情况下&#xff0c;不能读写对方的资源。同源策略规定了浏览器脚本互操作web数据的基本原则&#x…