hale
发布于 2023-08-02 / 58 阅读 / 0 评论 / 0 点赞

一切良好的开始-依赖注入

什么是依赖注入?

依赖注入解决了什么问题?

依赖注入是怎么使用的?

官方文档: 依赖关系注入 - .NET | Microsoft Docs

1.依赖注入是什么?

英文叫: Dependency Injection 简称DI

依赖关系注入 (DI) 是一种软件设计模式,这是一种在类及其依赖项之间实现控制反转 (IoC) 的技术。.NET 中的依赖关系注入是框架的内置部分,与配置、日志记录和选项模式一样。依赖项是指另一个对象所依赖的对象。

是这样一个过程:由于某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点。在程序运行过程中,客户类不直接实例化具体服务类实例,而是客户类的运行上下文环境或专门组件负责实例化服务类,然后将其注入到客户类中,保证客户类的正常运行。

2.依赖注入解决了什么问题?

依赖关系注入通过以下方式解决了这些问题:

  • 使用接口或基类将依赖关系实现抽象化。

  • 在服务容器中注册依赖关系。 ASP.NET Core 提供了一个内置的服务容器 IServiceProvider。服务通常在应用启动时注册,并追加到 IServiceCollection。 添加所有服务后,可以使用 BuildServiceProvider 创建服务容器。 服务通常已在应用的 Program.cs 文件中注册。

  • 将服务注入到使用它的类的构造函数中。 框架负责创建依赖关系的实例,并在不再需要时将其释放。

3.依赖注入是怎么使用的?

.net 内置DI框架的使用:

三种方式:(瞬时,范围,单例)所在包:Microsoft.Extensions.DependencyInjection;

  • AddTransient

  • AddScoped

  • AddSingleton

其他扩展方法:(多次添加不会报错)所在包:Microsoft.Extensions.DependencyInjection.Extensions;

  • TryAdd

  • TryAddTransient

  • TryAddScoped

  • TryAddSingleton

控制台程序使用依赖注入

文档示例方法:

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

namespace ConsoleDI.Example;

class Program
{
    static Task Main(string[] args)
    {
        using IHost host = CreateHostBuilder(args).Build();

        _ = host.Services.GetService<ExampleService>();

        return host.RunAsync();
    }

    static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((_, services) =>
                services.AddSingleton<IMessageWriter, ConsoleMessageWriter>()
                        .AddSingleton<IMessageWriter, LoggingMessageWriter>()
                        .AddSingleton<ExampleService>());
} 
// 控制台中服务注册的另一种方法
using Microsoft.Extensions.DependencyInjection;

ServiceCollection services = new ServiceCollection();
services.AddTransient<TestServiceImpl>();
services.AddScoped<ITestService>();
services.AddSingleton<ITestService, TestServiceImpl>();
using (ServiceProvider sp = services.BuildServiceProvider())
{
    var ts1 = sp.GetRequiredService<TestServiceImpl>();
    var ts2 = sp.GetRequiredService<TestServiceImpl>();
    Console.WriteLine(object.ReferenceEquals(ts1, ts2));
}

在.net WebApi项目中的应用

using ConfigSample.Options;
using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<PositionOptions>(builder.Configuration.GetSection(PositionOptions.Position));
builder.Services.Configure<ColorOptions>(builder.Configuration.GetSection(ColorOptions.Color));

builder.Services.AddScoped<IMyDependency, MyDependency>();
builder.Services.AddScoped<IMyDependency2, MyDependency2>();

var app = builder.Build();

服务注册方法

框架提供了适用于特定场景的服务注册扩展方法:

方法

自动对象 (object)释放

多种实现

传递参数

Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()

示例:

services.AddSingleton();

Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})

示例:

services.AddSingleton(sp => new MyDep());

services.AddSingleton(sp => new MyDep(99));

Add{LIFETIME}<{IMPLEMENTATION}>()

示例:

services.AddSingleton();

AddSingleton<{SERVICE}>(new {IMPLEMENTATION})

示例:

services.AddSingleton(new MyDep());

services.AddSingleton(new MyDep(99));

AddSingleton(new {IMPLEMENTATION})

示例:

services.AddSingleton(new MyDep());

services.AddSingleton(new MyDep(99));

// 这是一种工厂的创建方式,如果有传递参数的需求可以使用,一般第一种就可以满足 services.AddSingleton<IMyDep>(sp => new MyDep(99)); // 这种方式,DI框架不会管理对象的销毁,需要手动执行对象的销毁 services.AddSingleton(new MyDep());

避免使用服务定位器模式。 例如,可以使用 DI 代替时,不要调用 GetService 来获取服务实例:

// 不推荐使用服务定位器的方式获取服务,不推荐但是不代表绝对不能用 var myService = _service.GetService<IMyService>();

DI 是静态/全局对象访问模式的替代方法。 如果将其与静态对象访问混合使用,则可能无法意识到 DI 的优点。


评论