ASP.NET Core MVC依赖注入理解(极简个人版)
依赖注入
文献来源:《Pro ASP.NET Core MVC》 Adam Freeman 第18章 依赖注入
1 依赖注入原理
- 所有可能变化的地方都用接口
- 在使用接口的地方用什么实体类通过在
ConfigureService中注册解决 - 注册的实体类需要指定在何种生命周期中有效
- Transient
- Scoped
- Singleton
2 接口
接口只定义契约,不定义实现。

//IRepository.cs
using System.Collections.Generic;
namespace DependencyInjection.Models{public interface IRepository{IEnumerable<Product> Products{get;}Product this[string name]{get;}void AddProduct(Product product);void DeleteProduct(Product product);}
}//MemoryRepository.cs
using System.Collections.Generic;namespace DependencyInjection.Models{public class MemoryRepository:IRepository{private Dictionary<string, Product> products;public MemoryRepository(){products = new Dictionary<string, Product>();new List<Product{new Product{Name="Kayak", Price=275M},new Product{Name="Lifejacket", Price=48.95M},new Product{Name="Soccer ball", Price=19.50M},}.ForEach(p=>AddProduct(p));}public IEnumerable<Product> Products => products.Values;public Product this[string name] => products[name];public void AddProduct(Product product) => products[product.Name] = product;public void DeleteProduct(Product product) => products.Remove(product.Name);}
}
3 数据绑定页面
页面绑定代码使用@model和ViewBag实现。
//Index.cshtml
@using DependencyInjection.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model IEnumerable<Product>
@{layout=null;}<!DOCTYPE html>
<html><head><meta name="viewport" content="width=device-width" /><title>Dependency Injection</title><link rel="stylesheet" asp-href-include="lib/bootstrap/dist/css/*.min.css" /></head><body class="panel-body">@if(ViewData.Count>0){<table class="table table-bordered table-condense table-striped">@foreach(var kvp in ViewData){<tr><td>@kvp.Key</td><td>@kvp.Value</td></tr>}</table>}<table class="table table-bordered table-condense table-striped"><thead><tr><th>Name</th><th>Price</th></tr></thead><tbody>@if(Model==null){<tr><td colspan="3" class="text-center">No Model Data</td></tr>}else{@foreach(var p in Model){<tr><td>@p.Name</td><td>@string.Format("{0:C2}",p.Price)</td></tr>}}</tbody></table></body>
</html>
该页面绑定了2组数据集
ViewData集合@model IEnumerable<Product>
后台绑定
//HomeController.cs
//使用实体类的Products属性绑定页面
using Microsoft.AspNetCore.Mvc;
using DependencyInjection.Models;public class HomeController:Controller{public ViewResult Index() => View(new MemoryRepository().Products);
}
4 ASP.NET MVC定义依赖注入
4.1 接口依赖注入
- 单实例依赖注入
首先将后台处理由指定的实体类换成接口
//HomeController.cs
using Microsoft.AspNetCore.Mvc;
using DependencyInjection.Models;
using DependencyInjection.Infrastructure;namespace DependencyInjection.Controllers{//增加接口私有变量private IRepository repository;//通过构造函数传参至该私有变量public HomeController(IRepository repo){repository = repo;}//绑定接口的Products属性//运行时由ASP.NET MVC框架确定使用何种实体类public ViewResult Index()=>View(repository.Products);
}
然后配置接口与实体类之间的关系
//Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using DependencyInjection.Infrastructure;
using DependencyInjection.Model;namespace DependencyInjection{public void ConfigureServices(IServicesCollection services){services.AddTransient<IRepository, MemoryRepository>();...}...
}
services.AddTransient告诉service provider如何处理依赖。这里要注意一点,service provider是整个MVC框架服务的总提供者。
-
链式依赖
某实体类依赖于某接口,且该实体类中的成员变量又依赖于另一接口。

//MemoryRepository.cs
using DependencyInjection.Models{public class MemoryRepository:IRepository{private IModelStorage storage;//新增私有变量用于指定所属的仓库public MemoryStorage(IModelStorage storage){storage = modelStorage;new List<Product>{new Product{Name="Kayak",Price=275M},new Product{Name="Lifejacket", Price=48.95M},new Product{Name="Soccer ball", Price=19.50M}}.ForEach(p=>AddProduct(p));}public IEnumerable<Product> Products=>storage.Items;public Product this[string name] => storage[name];public void AddProduct(Product product){storage[product.Name] = product;}public void DeleteProduct(Product product){storage.RemoveItem(product.Name); }}
}
在ConfigureServices中注册。
//Startup.cs
...
namespace DependencyInjection{public class Startup{public void ConfigureServices(IServiceCollection services){services.AddTransient<IRepository, MemoryRepository>();services.AddTransient<IModelStorage, DictionaryStorage>();...}...}
}
4.2 实体类依赖注入
假设存在实体类ProductTotalizer用于计算总价。
using System.Linq;
namespace DependencyInjection.Models{public class ProductTotalizer{public ProductTotalizer(IRepository repo){Repository = repo;}//所述仓库用接口表示public IRepository Repository {get;set;}//总价public decimal Total => Repository.Products.Sum(p=>p.Price);}
}

在HomeController中加入ProductTotalizer变量。
...
public class HomeController:Controller{private IRepository repository;private ProductTotalizer totalizer;public HomeController(IRepository repo, ProductTotalizer total){repository = repo;totalizer = total;}public viewResult Index(){ViewBag.Total = totalizer.Total;return View(repository.Products);}
}
在Index.cshtml中包含了对ViewBag的显示,这里实际绑定了2个数据,第一个是ViewBag,第二个是repository。
为了明确在Controller中使用何种ProductTotalizer(可能在后面的开发中,ProductTotalizer是父类),可以对其进行强行指定。
//Startup.cs
...
public void ConfigureServices(IServicesCollection services){...services.AddTransient<ProductTotalizer>();...
}
5 服务生命周期
| 类别 | 生命周期 |
|---|---|
| Transient | 只要有调用就创建新的对象。就算类型一样,但是对象不一样 |
| Scoped | 在一个Controller之中,只要类型一样,对象就一样。刷新Controller之后对象变了,但是多个同一类型的对象还是同一个。 |
| Singleton | 只要初始化了,就一直是这个对象,不管是否刷新Controller |
5.1 Transient
首先在service provider中注册IRepository的调用使用MemoryRepository。
...
public void ConfigureServices(IServiceCollection services){services.AddTransient<IRepository, MemoryRepository>();...
}
...
在repository中加入ToString()函数,该函数的实现中包含了GUID的生成。
//MemoryRepository.cs
using System.Collections.Generic;
namespace DependencyInjection.Models{public class MemoryRepository:IRepository{private IModelStorage storage;private string guid = System.Guid.NewGuid().ToString();public MemoryRepository(IModelStorage modelStore){storage = modelStore;...}...public override string ToString(){return guid;}}
}
在下图中可以看到,当HomeController调用repository时,service provider会针对每一次调用都生成一个新的对象。

5.2 Scoped
其他的调用都一样,除了在service中注册使用AddScoped之外。
//Startup.cs
...
public void ConfigureServices(IServiceCollection services){service.AddScoped<IRepository, MemoryRepository>();...
}

5.3 Singleton
其他的调用都一样,除了在service中注册使用AddSingleton之外。
//Startup.cs
...
public void ConfigureServices(IServiceCollection services){services.AddSingleton<IRepository, MemoryRepository>();...
}
...

6 其他依赖注入
6.1 Action依赖注入
上述的依赖注入是针对整个Controller,但有的时候只想针对某一个Action进行依赖注入。此时只需要使用[FromServices]即可。
using Microsoft.AspNetCore.Mvc;
using DependencyInjection.Models;
using DependencyInjection.Infrastructure;namespace DependencyInjection.Controllers{public class HomeController:Controller{private IRepository repository;public HomeController(IRepository repo){repository = repo;}public ViewResult Index([FromServices]ProductTotalizer totalizer){ViewBag.HomeController = repository.ToString();ViewBag.Totalizer = totalizer.Repository.ToString();return View(repository.Products);}}
}
上述代码表示在Index中需要使用ProductTotalizer时,从service provider处获得。
6.2 手工进行依赖注入
除了上述的注册方式外,还有一些其他的注册方式。比如,当没有依赖出现时,而你又想获得一个接口的实例,该实例依赖于接口。在这种情况下,你可以直接通过service provider来实现。
...
public class HomeController:Controller{public ViewResult Index([FromServices]ProductTotalizer totalizer){IRepository repository=HttpContext.RequestServices.GetService<IRepository>();...}
}
...
相关文章:
ASP.NET Core MVC依赖注入理解(极简个人版)
依赖注入 文献来源:《Pro ASP.NET Core MVC》 Adam Freeman 第18章 依赖注入 1 依赖注入原理 所有可能变化的地方都用接口在使用接口的地方用什么实体类通过在ConfigureService中注册解决注册的实体类需要指定在何种生命周期中有效 TransientScopedSingleton 2…...
美光将于 2025 年推出 1γ DRAM,并在日本生产HBM
美国内存巨头美光正准备从 2025 年开始在其位于日本广岛的晶圆厂生产最先进的“1γ”DRAM。同时,公司计划在同一晶圆厂生产高带宽存储器(HBM),以满足对生成式人工智能应用日益增长的需求。 据《日经亚洲》12月13日报道࿰…...
【Docker】以service形式离线安装卸载的docker、compose服务
CentOS7离线卸载Docker步骤 移除开机自启 [rootCenOS-1 system]# systemctl disable docker移除注册文件 rm -rf /etc/systemd/system/docker.service删除相关安装目录 rm -rf $(find / -name docker)CentOS7离线安装Docker、Compose步骤 资源地址:docker_20.10…...
Dubbo RPC-Redis协议
Redis协议 特性说明 Redis 是一个高效的 KV 存储服务器。基于 Redis 实现的 RPC 协议。 2.3.0 以上版本支持。 使用场景 缓存,限流,分布式锁等 使用方式 引入依赖 从 Dubbo 3 开始,Redis 协议已经不再内嵌在 Dubbo 中,需要单…...
展开说说:Android之常用的延时执行策略
总结了以下六种常用的Android延时执行策略,以此记录: 1、TimerTask 2、Handler.postDelayed 3、Handler.sendEnptyMessageDelayeed 4、Thread.sleep线程休眠-需要在子线程 5、使用AlarmManager-全局定时器或者闹钟 6、Wait 首先定义一个时间常量&…...
Jenkins在window下配置Android打包配置
在Windows下配置Jenkins进行Android打包的步骤如下: 安装Jenkins:从Jenkins官网下载适用于Windows的安装包,并按照安装向导的指示完成安装。 启动Jenkins服务:启动Jenkins服务,确保服务正常运行。 配置Jenkins&#…...
云原生系列2-GitLab和Jenkins
1、GitLab类似github,是个私有仓库 1、GitLab安装,至少8G内存4核cpu # 查找Gitlab镜像 docker search gitlab/gitlab-ce # gitlab镜像拉取 docker pull gitlab/gitlab-ce # 查看镜像 docker images # 本机先建3个目录,为了gitlab容器通过挂…...
xcode无线真机调试详细图文步骤
步骤一、 步骤二: 步骤三: 配置完到这里,点击真机右键,菜单栏并未出现connect via ip address 选项,也没出现无线连接的小地球图标,别慌,接着进行下一步操作即可。 步骤四: 1.打开…...
EasyExcel合并相同内容单元格及动态标题功能的实现
一、最初版本 导出的结果: 对应实体类代码: import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ContentLoopMerge; import com.al…...
【论文解读】Comparing VVC, HEVC and AV1 using Objective and Subjective Assessments
时间:2020 级别:IEEE 机构: IEEE 组织 摘要: 对3种最新的视频编码标准HEVC (High Efficiency video Coding)测试模型HM (High Efficiency video Coding)、amedia video 1 (AV1)和Versatile video Coding测试模型 (VTM)进行了客观和…...
动态窗口法Dynamic Window Approach在动态环境中避障
以这个博主的代码为基础,加了一个碰撞检测,但是这个碰撞检测目前还不完善,思路应该是这个思路,以后有时间再完善吧。 动态窗口法:【路径规划】局部路径规划算法——DWA算法(动态窗口法)|&#…...
2023.12.15 FineBI与kettle
1.结构化就是可以用schema描述的数据,就是结构化数据,能转为二维表格, 如CSV,Excel, 2.半结构化就是部分可以转换为二维表格,如JSON,XML 3.非结构化数据,就是完全无法用二维表格表示的数据,如Word文档,Mp4,图片,等文件. kettle的流程 新建转换-构建流图-配置组件-保存运行 使…...
Python tkinter 初探Toplevel控件搭建父子窗口
目录 Toplevel控件搭建父子窗口 最简明的父子窗口框架 改进一:屏蔽和开放按钮 改进二:子窗口始终在主窗口之上 改进三:增加子窗口的关闭协议 改进四:使子窗口长获焦点 总结 Toplevel控件搭建父子窗口 最近,用P…...
SpringCloud源码探析(十二)-基于SpringBoot开发自定义中间件
1.概述 中间件是一种介于操作系统和应用软件之间,为应用软件提供服务功能的软件,按功能划分有消息中间件(Kafka、RocketMQ)、通信中间件(RPC通信中间件,dubbo等),应用服务器等。中间…...
基于CNN+数据增强+残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)+数据集+模型(一)
系列文章目录 基于CNN数据增强残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)数据集模型(一) 基于CNN数据增强残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)数据集模型…...
python实现贪吃蛇游戏
文章目录 1、项目说明2、项目预览3、开发必备4、贪吃蛇代码实现4.1、窗口和基本参数实现4.2、绘制背景4.3、绘制墙壁4.4、绘制贪吃蛇4.5、绘制食物4.6、实现长度信息显示4.7、定义游戏暂停界面4.8、定义贪吃蛇死亡界面4.9、实现贪吃蛇碰撞效果4.10、实现添加食物功能4.11、实现…...
ios备忘录怎么导入华为 方法介绍
作为一个常常需要在不同设备间切换的人,我深知备忘录的重要性。那些突如其来的灵感、重要的会议提醒、甚至是生活中的琐碎小事,我们都习惯性地记录在备忘录里。但当我决定从iPhone转向华为时,一个问题困扰了我:如何将那些珍贵的备…...
electron与cesium组件入门应用功能
electron与cesium组件入门应用功能 运行应用效果图: electron应用目录,需要包括三个文件: index.html main.js package.json (一)、创建一个新项目 目录名称:project_helloWolrd (二)、生成package.json文件 npm init --yes(三&#x…...
Jenkins Docker Cloud在Linux应用开发CI中的实践
Jenkins Docker Cloud在Linux应用开发CI中的实践 背景 通过代码提交自动触发CI自动构建、编译、打包是任何软件开发组织必不可少的基建,可以最大程度保证产物的一致性,方便跨组跨部门协作,代码MR等。 Docker在流水线中越来越重要ÿ…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
