当前位置: 首页 > news >正文

【微软技术栈】C#.NET 中使用依赖注入

本文内容

  1. 先决条件
  2. 创建新的控制台应用程序
  3. 添加接口
  4. 添加默认实现
  5. 添加需要 DI 的服务
  6. 为 DI 注册服务
  7. 结束语

本文介绍如何在 .NET 中使用依赖注入 (DI)。 借助 Microsoft 扩展,可通过添加服务并在 IServiceCollection 中配置这些服务来管理 DI。 IHost 接口会公开 IServiceProvider 实例,它充当所有已注册的服务的容器。

本文介绍如何执行下列操作:

  • 创建一个使用依赖注入的 .NET 控制台应用
  • 生成和配置通用主机
  • 编写多个接口及相应的实现
  • 为 DI 使用服务生存期和范围设定

1、先决条件

  • .NET Core 3.1 SDK 或更高版本。
  • 熟悉如何创建新的 .NET 应用程序以及如何安装 NuGet 包。

2、创建新的控制台应用程序

通过 dotnet new 命令或 IDE 的“新建项目”向导,新建一个名为 ConsoleDI 的 .NET 控制台应用程序 Example 。 将 NuGet 包 Microsoft.Extensions.Hosting 添加到项目。

新的控制台应用项目文件应如下所示:

<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net7.0</TargetFramework><Nullable>enable</Nullable><ImplicitUsings>true</ImplicitUsings><RootNamespace>ConsoleDI.Example</RootNamespace></PropertyGroup><ItemGroup><PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" /></ItemGroup></Project>

 重要

在此示例中,需要 NuGet 包 Microsoft.Extensions.Hosting 来生成和运行应用。 某些元包可能包含 Microsoft.Extensions.Hosting 包,在这种情况下,不需要显式包引用。

3、添加接口

在此示例应用中,你将了解依赖项注入如何处理服务生存期。 你将创建多个表示不同服务生存期的接口。 将以下接口添加到项目根目录:

IReportServiceLifetime.cs

using Microsoft.Extensions.DependencyInjection;namespace ConsoleDI.Example;public interface IReportServiceLifetime
{Guid Id { get; }ServiceLifetime Lifetime { get; }
}

IReportServiceLifetime 接口定义了以下项:

  • 表示服务的唯一标识符的 Guid Id 属性。
  • 表示服务生存期的 ServiceLifetime 属性。

IExampleTransientService.cs

using Microsoft.Extensions.DependencyInjection;namespace ConsoleDI.Example;public interface IExampleTransientService : IReportServiceLifetime
{ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Transient;
}

IExampleScopedService.cs

using Microsoft.Extensions.DependencyInjection;namespace ConsoleDI.Example;public interface IExampleScopedService : IReportServiceLifetime
{ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Scoped;
}

IExampleSingletonService.cs

using Microsoft.Extensions.DependencyInjection;namespace ConsoleDI.Example;public interface IExampleSingletonService : IReportServiceLifetime
{ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Singleton;
}

IReportServiceLifetime 的所有子接口均使用默认值显式实现 IReportServiceLifetime.Lifetime。 例如,IExampleTransientService 使用 ServiceLifetime.Transient 值显式实现 IReportServiceLifetime.Lifetime

4、添加默认实现

该示例实现使用 Guid.NewGuid() 的结果初始化其 Id 属性。 将各种服务的下列默认实现类添加到项目根目录:

ExampleTransientService.cs

namespace ConsoleDI.Example;internal sealed class ExampleTransientService : IExampleTransientService
{Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}

ExampleScopedService.cs

namespace ConsoleDI.Example;internal sealed class ExampleScopedService : IExampleScopedService
{Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}

ExampleSingletonService.cs

namespace ConsoleDI.Example;internal sealed class ExampleSingletonService : IExampleSingletonService
{Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}

每个实现都定义为 internal sealed 并实现其相应的接口。 例如,ExampleSingletonService 会实现 IExampleSingletonService

5、添加需要 DI 的服务

添加下列服务生存期报告器类,它作为服务添加到控制台应用:

ServiceLifetimeReporter.cs

namespace ConsoleDI.Example;internal sealed class ServiceLifetimeReporter
{private readonly IExampleTransientService _transientService;private readonly IExampleScopedService _scopedService;private readonly IExampleSingletonService _singletonService;public ServiceLifetimeReporter(IExampleTransientService transientService,IExampleScopedService scopedService,IExampleSingletonService singletonService) =>(_transientService, _scopedService, _singletonService) =(transientService, scopedService, singletonService);public void ReportServiceLifetimeDetails(string lifetimeDetails){Console.WriteLine(lifetimeDetails);LogService(_transientService, "Always different");LogService(_scopedService, "Changes only with lifetime");LogService(_singletonService, "Always the same");}private static void LogService<T>(T service, string message)where T : IReportServiceLifetime =>Console.WriteLine($"    {typeof(T).Name}: {service.Id} ({message})");
}

ServiceLifetimeReporter 会定义一个构造函数,该函数需要上述每一个服务接口(即 IExampleTransientServiceIExampleScopedService 和 IExampleSingletonService)。 对象会公开一个方法,使用者可通过该方法使用给定的 lifetimeDetails 参数报告服务。 被调用时,ReportServiceLifetimeDetails 方法会使用服务生存期消息记录每个服务的唯一标识符。 日志消息有助于直观呈现服务生存期。

6、为 DI 注册服务

使用以下代码更新 Program.cs:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ConsoleDI.Example;HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);builder.Services.AddTransient<IExampleTransientService, ExampleTransientService>();
builder.Services.AddScoped<IExampleScopedService, ExampleScopedService>();
builder.Services.AddSingleton<IExampleSingletonService, ExampleSingletonService>();
builder.Services.AddTransient<ServiceLifetimeReporter>();using IHost host = builder.Build();ExemplifyServiceLifetime(host.Services, "Lifetime 1");
ExemplifyServiceLifetime(host.Services, "Lifetime 2");await host.RunAsync();static void ExemplifyServiceLifetime(IServiceProvider hostProvider, string lifetime)
{using IServiceScope serviceScope = hostProvider.CreateScope();IServiceProvider provider = serviceScope.ServiceProvider;ServiceLifetimeReporter logger = provider.GetRequiredService<ServiceLifetimeReporter>();logger.ReportServiceLifetimeDetails($"{lifetime}: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()");Console.WriteLine("...");logger = provider.GetRequiredService<ServiceLifetimeReporter>();logger.ReportServiceLifetimeDetails($"{lifetime}: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()");Console.WriteLine();
}

每个 services.Add{LIFETIME}<{SERVICE}> 扩展方法添加(并可能配置)服务。 我们建议应用遵循此约定。 将扩展方法置于 Microsoft.Extensions.DependencyInjection 命名空间中以封装服务注册的组。 还包括用于 DI 扩展方法的命名空间部分 Microsoft.Extensions.DependencyInjection

  • 允许在不添加其他 using 块的情况下在 IntelliSense 中显示它们。
  • 在通常会调用这些扩展方法的 Program 或 Startup 类中,避免出现过多的 using 语句。

应用会执行以下操作:

  • 使用默认活页夹设置创建一个 IHostBuilder 实例。
  • 配置服务并对其添加相应的服务生存期。
  • 调用 Build() 并分配 IHost 的实例。
  • 调用 ExemplifyScoping,传入 IHost.Services。

7、结束语

在此示例应用中,你创建了多个接口和相应的实现。 其中每个服务都唯一标识并与 ServiceLifetime 配对。 示例应用演示了如何针对接口注册服务实现,以及如何在没有支持接口的情况下注册纯类。 然后,示例应用演示了如何在运行时解析定义为构造函数参数的依赖项。

运行该应用时,它会显示如下所示的输出:

// Sample output:
// Lifetime 1: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()
//     IExampleTransientService: d08a27fa-87d2-4a06-98d7-2773af886125 (Always different)
//     IExampleScopedService: 402c83c9-b4ed-4be1-b78c-86be1b1d908d (Changes only with lifetime)
//     IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
// ...
// Lifetime 1: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()
//     IExampleTransientService: b43d68fb-2c7b-4a9b-8f02-fc507c164326 (Always different)
//     IExampleScopedService: 402c83c9-b4ed-4be1-b78c-86be1b1d908d (Changes only with lifetime)
//     IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
// 
// Lifetime 2: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()
//     IExampleTransientService: f3856b59-ab3f-4bbd-876f-7bab0013d392 (Always different)
//     IExampleScopedService: bba80089-1157-4041-936d-e96d81dd9d1c (Changes only with lifetime)
//     IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
// ...
// Lifetime 2: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()
//     IExampleTransientService: a8015c6a-08cd-4799-9ec3-2f2af9cbbfd2 (Always different)
//     IExampleScopedService: bba80089-1157-4041-936d-e96d81dd9d1c (Changes only with lifetime)
//     IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)

在应用输出中,可看到:

  • Transient 服务总是不同的,每次检索服务时,都会创建一个新实例。
  • Scoped 服务只会随着新范围而改变,但在一个范围中是相同的实例。
  • Singleton 服务总是相同的,新实例仅被创建一次。

相关文章:

【微软技术栈】C#.NET 中使用依赖注入

本文内容 先决条件创建新的控制台应用程序添加接口添加默认实现添加需要 DI 的服务为 DI 注册服务结束语 本文介绍如何在 .NET 中使用依赖注入 (DI)。 借助 Microsoft 扩展&#xff0c;可通过添加服务并在 IServiceCollection 中配置这些服务来管理 DI。 IHost 接口会公开 IS…...

开启学历新征程,电大搜题助您轻松获取知识

作为一名电大学者&#xff0c;有肩负着传递真实信息、宣传正面价值的使命&#xff0c;而今天我要向您介绍的是一款非常实用的学习工具——电大搜题微信公众号。通过该平台&#xff0c;您可以获得更多关于浙江开放大学和广播电视大学的学习资源&#xff0c;助您在学习和工作上取…...

Redis 安装

前言 为什么需要学习如何安装Redis&#xff1f; 学习如何安装Redis对于软件开发人员来说是非常重要的&#xff0c;这是因为&#xff1a; 高效数据存储&#xff1a;Redis是一种高性能的键值存储系统&#xff0c;能够快速地存储和检索数据。学会安装Redis可以让开发人员和系统管…...

Windows GitBash解决Github添加密钥时提示Key is already in use的问题

通过添加多密钥实现 ssh-agent bashssh-keygen -t rsa -C ‘xx1’ -f ~/.ssh/id_rsa_xx1ssh-keygen -t rsa -C ‘xx2’ -f ~/.ssh/id_rsa_xx2ssh-add id_rsa_xx1ssh-add id_rsa_xx2 vim ~/.ssh/config Host github_xx1HostName github.comUser gitIdentityFile ~/.ssh/id_rs…...

第1关:简单查询

任务描述相关知识 检索数据表的内容编程要求测试说明 任务描述 本关任务&#xff1a; 用 SELECT 语句检索数据表中指定字段的数据&#xff1b; 用 SELECT 语句检索数据表中所有字段的数据。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.如何获取数据表…...

Android设计模式--Builder建造者模式

一&#xff0c;定义 Builder模式是一步一步创建一个复杂对象的创建型模式&#xff0c;它允许用户在不知道内部构建细节的情况下&#xff0c;可以更精细的控制对象的构造流程。 也就是将一个对象的构建与它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 二&…...

css实现鼠标悬停时元素的显示与隐藏

css实现鼠标悬停时元素的显示与隐藏 跟着B站黑马学习小兔鲜项目&#xff0c;有个点记录一下 就是当鼠标悬浮在商品列表上时&#xff0c;列表中的商品会显示出来&#xff0c;离开时&#xff0c;商品隐藏&#xff0c;如下&#xff1a; 感觉这个功能经常会遇到&#xff0c;但一直…...

天气越来越寒冷,一定要注意保暖

你们那里下雪了吗&#xff1f;听说西安已经下了今年的第一场雪&#xff0c;我们这里虽然隔了几百公里&#xff0c;但是只下雨没有下雪&#xff0c;不过气温是特别的冷&#xff0c;尤其是对我们这些上班族和上学的人而言&#xff0c;不管多冷&#xff0c;不管刮风下雨&#xff0…...

03 # 类型基础:动态类型与静态类型

通俗定义 静态类型语言&#xff1a;在编译阶段确定所有变量的类型 编译阶段确定属性偏移量用偏移量访问代替属性名访问偏移量信息共享 动态类型语言&#xff1a;在执行阶段确定所有变量的类型 在程序运行时&#xff0c;动态计算属性偏移量需要额外的空间存储属性名所有对象的…...

Python编程——模块、包和__init__.py

1. 模块 Python中的一个文件即为一个模块(Module)&#xff0c;一个模块引用另外一个模块的变量、函数或类时&#xff0c;使用import来导入。模块名即文件名。 如fibo.py 文件下有如下代码&#xff1a; def fib(n): # write Fibonacci series up to na, b 0, 1while a <…...

220kV110kV10kV变电站初步设计

摘要 由于国内人民生活水平的提高&#xff0c;科技不断地进步&#xff0c;控制不断地完善&#xff0c;从而促使变电站设计技术在电气系统领域占据主导权&#xff0c;也使得220kV/110kV/10kV变电站被广泛应用。在变电站系统设计领域中&#xff0c;220kV/110kV/10kV变电站成为目…...

Git企业开发级讲解(一)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、Git初识1、提出问题2、如何解决--版本控制器3、注意事项 二、Git 安装1、Linux-centos2、…...

【微信支付通知】对resource解密 AEAD_AES_256_GCM算法工具类

微信支付JSPIA支付-支付通知中&#xff0c;对resource解密 import javax.crypto.Cipher; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.Key; import java.util.Base64;public class AEADDecryption {public sta…...

JVM虚拟机:垃圾回收之三色标记

本文重点 在前面的课程中我们已经学习了垃圾回收器CMS和G1,其中CMS和G1中的mixedGC都存在四个过程,这四个过程中有一个过程叫做并发标记,也就是说程序一边运行,一边标记垃圾。这个过程最困难的是:如果在标记垃圾的时候,如果对象的引用关系发生了改变,此时应该如何处理?…...

唯坚持而已

写在前面 假如有一天我失业了&#xff1a; 大葱一毛二一斤&#xff0c;卖一三轮车三百斤还不到40块钱&#xff0c;我会回乡种大葱么&#xff1f; 小麦、玉米块儿八毛的一斤&#xff0c;亩产1000斤&#xff0c;五亩地&#xff0c;一年一茬小麦一茬玉米&#xff0c;才万把块钱&a…...

【大语言模型】Docker部署清华大学ChatGLM3教程

官方地址&#xff1a;https://github.com/THUDM/ChatGLM3 1 将代码保存至本地 方法1&#xff1a; git clone https://github.com/THUDM/ChatGLM3 方法2&#xff1a; https://github.com/THUDM/ChatGLM3/archive/refs/heads/main.zip 2 创建Docker文件 注&#xff1a;请先…...

详解 KEIL C51 软件的使用·设置工程·编绎与连接程序

详解 KEIL C51 软件的使用建立工程-CSDN博客 2. 设置工程 (1)在图 2-15 的画面中点击 会弹出如图 2-16 的对话框.其中有 10 个选择页.选择“Target” 项,也就是图 2-16 的画面. 图 2-16 在图 2-16 中,箭头所指的是晶振的频率值,默认是所选单片机最高的可用频率值.该设置值与单…...

小程序实现语音识别功能

不废话&#xff0c;直接上代码 <template><view><u-popupround"16" :show"recordShow" :close-on-click-overlay"false":safe-area-inset-bottom"false"close"close"open"open"><view clas…...

判断两层对象中是否有空的value值

1、方法 hasEmptyValue(obj) {for (var key in obj) {if (obj.hasOwnProperty(key)) {var value obj[key];// 检查第一层属性值是否为空if (value null || value undefined || value ) {return true;}// 检查第二层属性值是否为空if (typeof value object) {for (var inn…...

【SQLite】环境安装

SQLite - C/C SQLite简介 SQLite 是一种轻量级的嵌入式数据库引擎&#xff0c;它在程序中直接访问数据库文件而不需要独立的数据库服务器。以下是一些关于 SQLite 的简介信息&#xff1a; 嵌入式数据库引擎&#xff1a; SQLite 是一种嵌入式数据库引擎&#xff0c;这意味着它…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

深入浅出Diffusion模型:从原理到实践的全方位教程

I. 引言&#xff1a;生成式AI的黎明 – Diffusion模型是什么&#xff1f; 近年来&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;领域取得了爆炸性的进展&#xff0c;模型能够根据简单的文本提示创作出逼真的图像、连贯的文本&#xff0c;乃至更多令人惊叹的…...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

一些实用的chrome扩展0x01

简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序&#xff0c;无论是测试应用程序、搜寻漏洞还是收集情报&#xff0c;它们都能提升工作流程。 FoxyProxy 代理管理工具&#xff0c;此扩展简化了使用代理&#xff08;如 Burp…...

leetcode73-矩阵置零

leetcode 73 思路 记录 0 元素的位置&#xff1a;遍历整个矩阵&#xff0c;找出所有值为 0 的元素&#xff0c;并将它们的坐标记录在数组zeroPosition中置零操作&#xff1a;遍历记录的所有 0 元素位置&#xff0c;将每个位置对应的行和列的所有元素置为 0 具体步骤 初始化…...