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

2023-12-19 17:20

本文主要是介绍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依赖注入理解(极简个人版)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/513046

相关文章

Java Spring的依赖注入理解及@Autowired用法示例详解

《JavaSpring的依赖注入理解及@Autowired用法示例详解》文章介绍了Spring依赖注入(DI)的概念、三种实现方式(构造器、Setter、字段注入),区分了@Autowired(注入... 目录一、什么是依赖注入(DI)?1. 定义2. 举个例子二、依赖注入的几种方式1. 构造器注入(Con

如何使用Lombok进行spring 注入

《如何使用Lombok进行spring注入》本文介绍如何用Lombok简化Spring注入,推荐优先使用setter注入,通过注解自动生成getter/setter及构造器,减少冗余代码,提升开发效... Lombok为了开发环境简化代码,好处不用多说。spring 注入方式为2种,构造器注入和setter

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM

IDEA Maven提示:未解析的依赖项的问题及解决

《IDEAMaven提示:未解析的依赖项的问题及解决》:本文主要介绍IDEAMaven提示:未解析的依赖项的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录IDEA Maven提示:未解析的依编程赖项例如总结IDEA Maven提示:未解析的依赖项例如

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以

Java -jar命令如何运行外部依赖JAR包

《Java-jar命令如何运行外部依赖JAR包》在Java应用部署中,java-jar命令是启动可执行JAR包的标准方式,但当应用需要依赖外部JAR文件时,直接使用java-jar会面临类加载困... 目录引言:外部依赖JAR的必要性一、问题本质:类加载机制的限制1. Java -jar的默认行为2. 类加

java -jar命令运行 jar包时运行外部依赖jar包的场景分析

《java-jar命令运行jar包时运行外部依赖jar包的场景分析》:本文主要介绍java-jar命令运行jar包时运行外部依赖jar包的场景分析,本文给大家介绍的非常详细,对大家的学习或工作... 目录Java -jar命令运行 jar包时如何运行外部依赖jar包场景:解决:方法一、启动参数添加: -Xb