当前位置: 首页 > 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 服务总是相同的,新实例仅被创建一次。
http://www.lryc.cn/news/230419.html

相关文章:

  • 开启学历新征程,电大搜题助您轻松获取知识
  • Redis 安装
  • Windows GitBash解决Github添加密钥时提示Key is already in use的问题
  • 第1关:简单查询
  • Android设计模式--Builder建造者模式
  • css实现鼠标悬停时元素的显示与隐藏
  • 天气越来越寒冷,一定要注意保暖
  • 03 # 类型基础:动态类型与静态类型
  • Python编程——模块、包和__init__.py
  • 220kV110kV10kV变电站初步设计
  • Git企业开发级讲解(一)
  • 【微信支付通知】对resource解密 AEAD_AES_256_GCM算法工具类
  • JVM虚拟机:垃圾回收之三色标记
  • 唯坚持而已
  • 【大语言模型】Docker部署清华大学ChatGLM3教程
  • 详解 KEIL C51 软件的使用·设置工程·编绎与连接程序
  • 小程序实现语音识别功能
  • 判断两层对象中是否有空的value值
  • 【SQLite】环境安装
  • 【QT】飞机大战
  • linux最全基础入门命令(简单明了建议收藏)
  • dgl 的cuda 版本 环境配置(dgl cuda 版本库无法使用问题解决)
  • 回文数和复利的威力(C#)
  • 【Java】面向对象程序设计 课程笔记 Java核心类
  • 16个值得推荐的.NET ORM框架
  • Git 进阶使用
  • 【微软技术栈】C#.NET 泛型数学
  • 【nlp】1.1文本处理的基本方法
  • 流量分析(信息安全铁人三项赛分区赛2-5.18)
  • 云服务器如何选?腾讯云2核2G3M云服务器88元一年!