ASP.NET Core MVC依赖注入理解(极简个人版)

news2024/12/24 22:01:54

依赖注入

文献来源:《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>();
			...
	}
}
...

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

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

相关文章

开源学习项目推荐

文章目录 koodo-reader凤凰架构学习项目NPS 内网穿透客户端 koodo-reader 项目地址&#xff1a;https://github.com/koodo-reader/koodo-reader 介绍&#xff1a;一个开源的阅读器&#xff0c;阅读pdf也有目录&#xff0c;作为epub阅读器和pdf阅读器看资料挺好 凤凰架构 项…

NPDP证书含金量高吗?跟PMP相比含金量怎么样?

两个证方向不太一样&#xff0c;含金量都挺高的&#xff0c;具体怎么选呢&#xff1f;接着往下看~ PS&#xff1a;不想看长篇大论的&#xff0c;来找我&#xff0c;直接把你的经历甩出来&#xff0c;我帮你判断~ 一、产品经理跟项目经理的区别 表面上&#xff0c;项目经理和产…

WEB渗透—PHP反序列化(四)

Web渗透—PHP反序列化 课程学习分享&#xff08;课程非本人制作&#xff0c;仅提供学习分享&#xff09; 靶场下载地址&#xff1a;GitHub - mcc0624/php_ser_Class: php反序列化靶场课程&#xff0c;基于课程制作的靶场 课程地址&#xff1a;PHP反序列化漏洞学习_哔哩…

7+单细胞+分型+机器学习,最近大热的生信思路,要拿分趁现在

今天给同学们分享一篇生信文章“Machine learning-based integration develops a neutrophil-derived signature for improving outcomes in hepatocellular carcinoma”&#xff0c;这篇文章发表在Front Immunol期刊上&#xff0c;影响因子为7.3。 结果解读&#xff1a; 单细…

GaN图腾柱无桥 Boost PFC(单相)九-EMI 滤波器容性电流影响分析

前言 为了防止 PFC 变换器中高频开关谐波对电网产生影响&#xff0c;同时抑制电网中的高频干扰对变换器运行的影响&#xff0c;一般通过在 PFC 变换器与交流电源之间加入EMI 滤波器消除共模干扰和差模干扰&#xff0c;使变换器满足相应的 EMI 标准。在基于GaN 功率器件的图腾柱…

A01、深入了解性能优化

1、常用性能评价/测试指标 1.1、响应时间 提交请求和返回该请求的响应时间之间使用的时间&#xff0c;一般比较关注平均响应时间。常用操作的响应时间列表&#xff1a; 操作响应时间打开一个站点几秒数据库查询一条记录&#xff08;有索引&#xff09;十几毫秒机械磁盘一次寻…

三菱PLC FX3U滑动平均值滤波

三菱PLC滑动平均值滤波其它相关写法,请参考下面文章链接: https://rxxw-control.blog.csdn.net/article/details/125044013https://rxxw-control.blog.csdn.net/article/details/125044013滑动平均值滤波程序总共分为三部分,第一步为:滑动采样。 第二步为:队列求和,第三…

坐标前后限制转点的坐标取值+网络流拆维拆点:agc031_e

https://vj.imken.moe/contest/598718#problem/J 观察到数据范围很小&#xff0c;但一个很重要的信息我们缺失了&#xff0c;就是珠宝的数量&#xff0c;所以我们考虑枚举珠宝的数量 k k k。 对于横纵坐标什么至多至少的限制&#xff0c;比如 a i a_i ai​ 前最多偷 b i b…

openwrt docker nginx 站点搭建

应为家里一直是 openwrt 软路由&#xff0c;这样以来也不用 重新买服务器了&#xff0c;就直接在 openwrt 上面跑个 nginx就行了。把自己的一些东西就可以放上面了。资源再利用哈哈&#xff1b; 先 ssh 连接上 openwrt &#xff1a;我这里的 openwrt 最近刚更新的固件&#xff…

confluence 备份与恢复

备份 confluence 每天会自动备份文件到 /var/atlassian/application-data/confluence/backups 新增定时任务&#xff0c;每天将备份的文件拷贝到远程服务器: crontab -l 0 0 3 * * ? sh /var/atlassian/application-data/confluence/backups/backup.sh#!/bin/shscp_linux_pa…

Ansys Speos SSS|传感器特性与EMVA1288标准以及Lumerical传感器验证

附件下载 联系工作人员获取附件 概述 本文是Speos Sensor System exporter&#xff08;SSS&#xff09;的使用指南&#xff0c;这是一个强大的解决方案&#xff0c;用于camera sensor模拟结果的后处理。本文介绍了一组实际示例&#xff0c;以演示该工具基于EMVA 1288标准从传…

鸿蒙OS:打破界限的操作系统新星

导言 鸿蒙OS&#xff08;HarmonyOS&#xff09;是华为公司为应对技术封锁而推出的分布式操作系统&#xff0c;其背后蕴含着华为构建全球数字生活愿景的雄心。本文将深入剖析鸿蒙OS的起源、核心特性&#xff0c;并展望其未来在数字生态中的角色。 1. 背景与起源 华为的…

Ubuntu 常用命令之 sed 命令用法介绍

sed是一个在Linux和其他Unix-like系统中常用的流编辑器&#xff0c;用于对输入流&#xff08;文件或管道&#xff09;进行基本的文本转换。它可以非常方便地进行文本替换、插入、删除等操作。 sed命令的基本格式为 sed [options] command file(s)其中&#xff0c;常用的参数有…

【回溯】【回文字符串】131.分割回文串

题目 法1&#xff1a;DFS双指针 必须掌握基础方法&#xff01; 注意&#xff1a;使用ArrayList删除尾元素比LinkedList要快很多&#xff01;&#xff01;&#xff01; class Solution {public List<List<String>> partition(String s) {List<List<String&…

2023/12 拜个师傅带你学算法写论文

文章目录 1. 自我介绍2. 你是否遇到如下问题3. 解决方案4. Tips5. 同学的学习历程&#xff08;每日更新&#xff09;2023/12/18 关注公众号&#xff1a;『AI学习星球』 算法学习、4对1辅导、论文辅导或核心期刊可以通过公众号或CSDN滴滴我 1. 自我介绍 本人是985大学计算机硕…

【AIGC重塑教育】AI大模型驱动的教育变革与实践

文章目录 &#x1f354;现状&#x1f6f8;解决方法✨为什么要使用ai&#x1f386;彩蛋 &#x1f354;现状 AI正迅猛地改变着我们的生活。根据高盛发布的一份报告&#xff0c;AI有可能取代3亿个全职工作岗位&#xff0c;影响全球18%的工作岗位。在欧美&#xff0c;或许四分之一…

天猫数据分析-天猫分析工具-天猫数据在哪里看-11月天猫宝宝辅食行业销售分析报告

随着居民可支配收入的增长&#xff0c;家长对宝宝健康的关注程度也随之上涨&#xff0c;尽管出生率下降&#xff0c;但越来越多的家长逐渐认可外购辅食的价值&#xff0c;因此辅食也逐渐“刚需化”&#xff0c;辅食市场也保持着较大的市场规模。 根据鲸参谋电商数据分析平台的相…

【MySQL】 表的操作

// 创建表 create table 表名();// 查看表结构 desc 表名;// 新增一列表信息 alter table 表名 add 字段名 字段类型 (after 原表某一字段名);// 删除一列表信息 alter table 表名 drop 字段名;// 修改表字段名字 alter table 表名 change 原字段名 新字段名 类型; // 新字…

Ubuntu安装ARM交叉编译器

Ubuntu安装交叉编译器 更新apt # 更新apt sudo apt update安装gcc sudo apt install build-essential查看gcc版本 gcc -v下载交叉编译工具 复制到用户目录 解压 tar -xvf gcc-linaro-5.5.0-2017.10-x86_64_arm-linux-gnueabihf.tar.xz移动到/opt/下 sudo ./gcc-linaro-5.…

分享一个好看的vs主题

最近发现了一个很好看的vs主题&#xff08;个人认为挺好看的&#xff09;&#xff0c;想要分享给大家。 主题的名字叫NightOwl&#xff0c;和vscode的主题颜色挺像的。操作方法也十分简单&#xff0c;首先我们先在最上面哪一行找到扩展。 然后点击管理扩展&#xff0c;再搜索栏…