赞
踩
本文介绍如何在 .NET 中使用依赖注入 (DI)。 借助 Microsoft 扩展,可通过添加服务并在 IServiceCollection 中配置这些服务来管理 DI。 IHost 接口会公开 IServiceProvider 实例,它充当所有已注册的服务的容器。
本文介绍如何执行下列操作:
通过 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
包,在这种情况下,不需要显式包引用。
在此示例应用中,你将了解依赖项注入如何处理服务生存期。 你将创建多个表示不同服务生存期的接口。 将以下接口添加到项目根目录:
IReportServiceLifetime.cs
- using Microsoft.Extensions.DependencyInjection;
-
- namespace ConsoleDI.Example;
-
- public interface IReportServiceLifetime
- {
- Guid Id { get; }
-
- ServiceLifetime Lifetime { get; }
- }
IReportServiceLifetime
接口定义了以下项:
Guid Id
属性。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
。
该示例实现使用 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
。
添加下列服务生存期报告器类,它作为服务添加到控制台应用:
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
会定义一个构造函数,该函数需要上述每一个服务接口(即 IExampleTransientService
、IExampleScopedService
和 IExampleSingletonService
)。 对象会公开一个方法,使用者可通过该方法使用给定的 lifetimeDetails
参数报告服务。 被调用时,ReportServiceLifetimeDetails
方法会使用服务生存期消息记录每个服务的唯一标识符。 日志消息有助于直观呈现服务生存期。
使用以下代码更新 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
语句。应用会执行以下操作:
ExemplifyScoping
,传入 IHost.Services。在此示例应用中,你创建了多个接口和相应的实现。 其中每个服务都唯一标识并与 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)
在应用输出中,可看到:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。