• 在 .NET 中使用依赖注入(转载)


    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;
    }

    }

      

  • 相关阅读:
    VCC、VDD、VEE等区别
    Matlab运行速度/效率受哪些因素影响?
    strtok/atoi/atof/atol函数用法 详解
    双色球随机生成
    万能指针void*学习
    空指针和 指向指针的指针
    指针运算
    快速编译c/cpp文件
    贪吃蛇(C)
    判断规定时间内有无输入
  • 原文地址:https://www.cnblogs.com/huawublog/p/15933194.html
Copyright © 2020-2023  润新知