惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
爱范儿
爱范儿
J
Java Code Geeks
L
LangChain Blog
V
V2EX
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
博客园 - Franky
Microsoft Azure Blog
Microsoft Azure Blog
Jina AI
Jina AI
Blog — PlanetScale
Blog — PlanetScale
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The Cloudflare Blog
博客园 - 司徒正美
B
Blog
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
Engineering at Meta
Engineering at Meta
MyScale Blog
MyScale Blog
有赞技术团队
有赞技术团队
Hugging Face - Blog
Hugging Face - Blog

博客园 - 我的用户名

解决方案:包 Avalonia.Android 11.x.x 与 net8.0-android21.0 (.NETCoreApp,Version=v8.0) 不兼容。 包 Avalonia.Android11.x.x 支持: net8.0-android34.0 (.NETCoreApp,Version=v8.0) avalonia android TextBox多行模式下回车会关闭输入法问题 avalonia ComboBox 绑定后无法选中项的问题 Winform 使用label画分割线 VS-PostBuildEvent常用示例 bat脚本,请求管理员权限(转载) 在 ASP.NET Core 中使用多个环境(转载) .NET Core 和 ASP.NET Core 中的日志记录(转载) RSA 从私钥中获取公钥 CorrelationId 文档 C# 简繁互转 JT808-2019协议,协议消息ID .Net开发中不太常用的DLL及用法 分享一个dotnet自动发布Docker的脚本 VS常用设置 NuGet修改默认包保存的位置 winform下UserControl未标记为可序列化问题 AgileConfig服务端搭建 IE浏览器,各版本的请求头信息
在 .NET 中使用依赖注入(转载)
我的用户名 · 2022-02-24 · via 博客园 - 我的用户名

https://m.toutiao.com/is/LEPUyXp/?=在 .NET 中使用依赖注入 - 今日头条

如何在 .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(
            $"{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($"{scope}-Call 1 .GetRequiredService<OperationLogger>()");

    Console.WriteLine("...");

    logger = provider.GetRequiredService<OperationLogger>();
    logger.LogOperations($"{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 操作总是相同,新实例仅被创建一次。

ActivatorUtilities

 无法自动注入的类想使用DI提供的类时可使用此类创建对应的实例

var testTask = ActivatorUtilities.CreateInstance<TestTask>(serProvider, "test");
testTask.Execute(new CancellationToken());

public class TestTask : ITask
{
private readonly IServiceScopeFactory _serviceScopeFactory;

private readonly IMyHostService myHostService;

private readonly string _name;

public TestTask(IServiceScopeFactory serviceScopeFactory,
IMyHostService myHostService,
string name)
{
_serviceScopeFactory = serviceScopeFactory;
this.myHostService = myHostService;
_name = name;
}

}