百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

在 .NET 中使用依赖注入

bigegpt 2024-08-15 20:01 2 浏览

如何在 .NET 中使用依赖注入 (DI)。 使用 Microsoft 扩展时,DI 是“一等公民”,其中服务是在 IServiceCollection 中添加和配置的。 IHost 接口会公开 IServiceProvider 实例,它充当所有已注册的服务的容器。

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

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

先决条件

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

创建新的控制台应用程序

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

添加接口

将以下接口添加到项目根目录:

IOperation.cs

C#

namespace ConsoleDI.Example;

public interface IOperation
{
    string OperationId { get; }
}

IOperation 接口会定义一个 OperationId 属性。

IOperation.cs Transient

C#

namespace ConsoleDI.Example;

public interface ITransientOperation : IOperation
{
}

IOperation.cs Scoped

C#

namespace ConsoleDI.Example;

public interface IScopedOperation : IOperation
{
}

IOperation.cs Singleton

C#

namespace ConsoleDI.Example;

public interface ISingletonOperation : IOperation
{
}

IOperation 的所有子接口会会命名其预期服务生存期。 例如,“Transient”或“Singleton”。

添加默认实现

添加以下默认实现来进行各种操作:

DefaultOperation.cs

C#

using static System.Guid;

namespace ConsoleDI.Example;

public class DefaultOperation :
    ITransientOperation,
    IScopedOperation,
    ISingletonOperation
{
    public string OperationId { get; } = NewGuid().ToString()[^4..];
}

DefaultOperation 会实现所有已命名的标记接口,并将 OperationId 属性初始化为新的全局唯一标识符 (GUID) 的最后 4 个字符。

添加需要 DI 的服务

添加以下操作记录器对象,它作为服务添加到控制台应用:

OperationLogger.cs

C#

namespace ConsoleDI.Example;

public class OperationLogger
{
    private readonly ITransientOperation _transientOperation;
    private readonly IScopedOperation _scopedOperation;
    private readonly ISingletonOperation _singletonOperation;

    public OperationLogger(
        ITransientOperation transientOperation,
        IScopedOperation scopedOperation,
        ISingletonOperation singletonOperation) =>
        (_transientOperation, _scopedOperation, _singletonOperation) =
            (transientOperation, scopedOperation, singletonOperation);

    public void LogOperations(string scope)
    {
        LogOperation(_transientOperation, scope, "Always different");
        LogOperation(_scopedOperation, scope, "Changes only with scope");
        LogOperation(_singletonOperation, scope, "Always the same");
    }


    private static void LogOperation<T>(T operation, string scope, string message)
        where T : IOperation =>
        Console.WriteLine(
            #34;{scope}: {typeof(T).Name,-19} [ {operation.OperationId}...{message,-23} ]");
}

OperationLogger 会定义一个构造函数,该函数需要上述每一个标记接口(即 ITransientOperation、IScopedOperation 和 ISingletonOperation)。 对象会公开一个方法,使用者可通过该方法使用给定的 scope 参数记录操作。 被调用时,LogOperations 方法会使用范围字符串和消息记录每个操作的唯一标识符。

为 DI 注册服务

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

C#

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ConsoleDI.Example;

using IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((_, services) =>
        services.AddTransient<ITransientOperation, DefaultOperation>()
            .AddScoped<IScopedOperation, DefaultOperation>()
            .AddSingleton<ISingletonOperation, DefaultOperation>()
            .AddTransient<OperationLogger>())
    .Build();

ExemplifyScoping(host.Services, "Scope 1");
ExemplifyScoping(host.Services, "Scope 2");

await host.RunAsync();

static void ExemplifyScoping(IServiceProvider services, string scope)
{
    using IServiceScope serviceScope = services.CreateScope();
    IServiceProvider provider = serviceScope.ServiceProvider;

    OperationLogger logger = provider.GetRequiredService<OperationLogger>();
    logger.LogOperations(#34;{scope}-Call 1 .GetRequiredService<OperationLogger>()");

    Console.WriteLine("...");

    logger = provider.GetRequiredService<OperationLogger>();
    logger.LogOperations(#34;{scope}-Call 2 .GetRequiredService<OperationLogger>()");

    Console.WriteLine();
}

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

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

应用会执行以下操作:

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

结束语

应用会显示如下例所示的输出:

控制台

Scope 1-Call 1 .GetRequiredService<OperationLogger>(): ITransientOperation [ 80f4...Always different        ]
Scope 1-Call 1 .GetRequiredService<OperationLogger>(): IScopedOperation    [ c878...Changes only with scope ]
Scope 1-Call 1 .GetRequiredService<OperationLogger>(): ISingletonOperation [ 1586...Always the same         ]
...
Scope 1-Call 2 .GetRequiredService<OperationLogger>(): ITransientOperation [ f3c0...Always different        ]
Scope 1-Call 2 .GetRequiredService<OperationLogger>(): IScopedOperation    [ c878...Changes only with scope ]
Scope 1-Call 2 .GetRequiredService<OperationLogger>(): ISingletonOperation [ 1586...Always the same         ]

Scope 2-Call 1 .GetRequiredService<OperationLogger>(): ITransientOperation [ f9af...Always different        ]
Scope 2-Call 1 .GetRequiredService<OperationLogger>(): IScopedOperation    [ 2bd0...Changes only with scope ]
Scope 2-Call 1 .GetRequiredService<OperationLogger>(): ISingletonOperation [ 1586...Always the same         ]
...
Scope 2-Call 2 .GetRequiredService<OperationLogger>(): ITransientOperation [ fa65...Always different        ]
Scope 2-Call 2 .GetRequiredService<OperationLogger>(): IScopedOperation    [ 2bd0...Changes only with scope ]
Scope 2-Call 2 .GetRequiredService<OperationLogger>(): ISingletonOperation [ 1586...Always the same         ]

在应用输出中,可看到:

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

相关推荐

了解Linux目录,那你就了解了一半的Linux系统

大到公司或者社群再小到个人要利用Linux来开发产品的人实在是多如牛毛,每个人都用自己的标准来配置文件或者设置目录,那么未来的Linux则就是一团乱麻,也对管理造成许多麻烦。后来,就有所谓的FHS(F...

Linux命令,这些操作要注意!(linux命令?)

刚玩Linux的人总觉得自己在演黑客电影,直到手滑输错命令把公司服务器删库,这才发现命令行根本不是随便乱用的,而是“生死簿”。今天直接上干货,告诉你哪些命令用好了封神!喜欢的一键三连,谢谢观众老爷!!...

Linux 命令速查手册:这 30 个高频指令,拯救 90% 的运维小白!

在Linux系统的世界里,命令行是强大的武器。对于运维小白而言,掌握一些高频使用的Linux命令,能极大提升工作效率,轻松应对各种系统管理任务。今天,就为大家奉上精心整理的30个Linu...

linux必学的60个命令(linux必学的20个命令)

以下是Linux必学的20个基础命令:1.cd:切换目录2.ls:列出文件和目录3.mkdir:创建目录4.rm:删除文件或目录5.cp:复制文件或目录6.mv:移动/重命名文件或目录7....

提高工作效率的--Linux常用命令,能够决解95%以上的问题

点击上方关注,第一时间接受干货转发,点赞,收藏,不如一次关注评论区第一条注意查看回复:Linux命令获取linux常用命令大全pdf+Linux命令行大全pdf为什么要学习Linux命令?1、因为Li...

15 个实用 Linux 命令(linux命令用法及举例)

Linux命令行是系统管理员、开发者和技术爱好者的强大工具。掌握实用命令不仅能提高效率,还能解锁Linux系统的无限潜力,本文将深入介绍15个实用Linux命令。ls-列出目录内容l...

Linux 常用命令集合(linux常用命令全集)

系统信息arch显示机器的处理器架构(1)uname-m显示机器的处理器架构(2)uname-r显示正在使用的内核版本dmidecode-q显示硬件系统部件-(SMBIOS/DM...

Linux的常用命令就是记不住,怎么办?

1.帮助命令1.1help命令#语法格式:命令--help#作用:查看某个命令的帮助信息#示例:#ls--help查看ls命令的帮助信息#netst...

Linux常用文件操作命令(linux常用文件操作命令有哪些)

ls命令在Linux维护工作中,经常使用ls这个命令,这是最基本的命令,来写几条常用的ls命令。先来查看一下使用的ls版本#ls--versionls(GNUcoreutils)8.4...

Linux 常用命令(linux常用命令)

日志排查类操作命令查看日志cat/var/log/messages、tail-fxxx.log搜索关键词grep"error"xxx.log多条件过滤`grep-E&#...

简单粗暴收藏版:Linux常用命令大汇总

号主:老杨丨11年资深网络工程师,更多网工提升干货,请关注公众号:网络工程师俱乐部下午好,我的网工朋友在Linux系统中,命令行界面(CLI)是管理员和开发人员最常用的工具之一。通过命令行,用户可...

「Linux」linux常用基本命令(linux常用基本命令和用法)

Linux中许多常用命令是必须掌握的,这里将我学linux入门时学的一些常用的基本命令分享给大家一下,希望可以帮助你们。总结送免费学习资料(包含视频、技术学习路线图谱、文档等)1、显示日期的指令:d...

Linux的常用命令就是记不住,怎么办?于是推出了这套教程

1.帮助命令1.1help命令#语法格式:命令--help#作用:查看某个命令的帮助信息#示例:#ls--help查看ls命令的帮助信息#netst...

Linux的30个常用命令汇总,运维大神必掌握技能!

以下是Linux系统中最常用的30个命令,精简版覆盖日常操作核心需求,适合快速掌握:一、文件/目录操作1.`ls`-列出目录内容`ls-l`(详细信息)|`ls-a`(显示隐藏文件)...

Linux/Unix 系统中非常常用的命令

Linux/Unix系统中非常常用的命令,它们是进行文件操作、文本处理、权限管理等任务的基础。下面是对这些命令的简要说明:**文件操作类:*****`ls`(list):**列出目录内容,显...