Инверсия зависимостей (DI)

Инверсия зависимостей (Dependency injection) - очень мощный инструмент управления приложением. Позволяет управлять логикой создания реализаций в приложении. Для этого в приложение создают контейнер с зависимостями в котором регистрируют все связки интерфейс-реализация, после из этого контейнера можно получить зависимость по интерфейсу, если для построения требуемой зависимости необходимы другие интерфейсы, то они инициализируются в соответствии с прочими зарегистрированными связками. Это позволяет отвязаться от конкретных реализаций в зависимостях и конфигурировать их заранее

<aside> 🐺 Подробней про это можно почитать тут https://learn.microsoft.com/ru-ru/dotnet/architecture/modern-web-apps-azure/architectural-principles#dependency-inversion.

</aside>

Давайте попробуем:

  1. Давайте попробуем воспользоваться им:

    dotnet new console -o lesson10
    
  2. Заменим код Program.cs на:

    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Options;
    
    using var host = Host.CreateDefaultBuilder(args)
        .ConfigureServices(services =>
        {
            services.AddOptions<AppSettings>().BindConfiguration(nameof(AppSettings));
        })
        .Build();
    
    var options = host.Services.GetRequiredService<IOptions<AppSettings>>();
    
    Console.WriteLine($"{options.Value.FirstWord} {options.Value.SecondWord} {options.Value.Versions[0].Number}");
    
  3. Добавим файл AppSettings.cs:

    public class AppSettings
    {
        public string? FirstWord { get; set; }
    
        public string? SecondWord { get; set; }
    
        public class Version
        {
            public int Number { get; set; }
        }
    
        public List<Version> Versions { get; set; } = new();
    }
    
  4. Создайте новый файл appsettings.json в том же каталоге, что и ваш файл Program.cs.

    {
        "AppSettings": {
            "FirstWord": "Hello",
            "SecondWord": "dotnet",
            "Versions": [
                {
                    "Number": 7
                }
            ]
        }
    }
    
  5. Создайте новый файл appsettings.Development.json в том же каталоге, что и ваш файл Program.cs.

    {
        "AppSettings": {
            "Versions": [
                {
                    "Number": 8
                }
            ]
        }
    }
    
  6. В консоле перейдите в папку с проектом.

  7. Установим зависимость через dotnet cli:

    dotnet add package Microsoft.Extensions.Hosting --version 7.0.1
    

    Добавление пакетов отразится в файле lesson10.csproj

  8. Вместо имени файла укажите шаблон appsettings*.json в csproj, все файлы подходящие под него будут копироваться в директорию при сборке проекта. Давайте добавим нужную настройку в lesson10.csproj.

    <ItemGroup>
      <Content Include="appsettings*.json">
        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      </Content>
    </ItemGroup>
    

    Должно получиться следующее:

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net7.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
      </ItemGroup>
    
      <ItemGroup>
        <Content Include="appsettings*.json">
          <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
      </ItemGroup>
    
    </Project>
    

    В этом уроке мы подключаем DI и добавить в него конфигурацию. На что тут стоит обратить внимание:

  9. Импортировали зависимость Microsoft.Extensions.DependencyInjection - нужна для использования методов AddOptions и GetRequiredService. Метод AddOptions позволяет добавить в DI конфигурацию. Метод GetRequiredService позволяет получить из DI любую зависимость, в случае если зависимости нет будет выброшено исключение.

    <aside> 🐺 Подробней про AddOptions можно почитать тут https://learn.microsoft.com/ru-ru/aspnet/core/fundamentals/configuration/options?view=aspnetcore-7.0#optionsbuilder-api.

    </aside>

    <aside> 🐺 Подробней про GetRequiredService можно почитать тут https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.serviceproviderserviceextensions.getrequiredservice?view=dotnet-plat-ext-7.0.

    </aside>

  10. Импортировали зависимость Microsoft.Extensions.Options - нужна для использования класса IOptions<T>.

    <aside> 🐺 Подробней про IOptions<T> и другие его разновидности можно почитать тут https://learn.microsoft.com/ru-ru/aspnet/core/fundamentals/configuration/options?view=aspnetcore-7.0#options-interfaces.

    </aside>

  11. Метод ConfigureServices зависимости Microsoft.Extensions.Hosting позволяет настроить DI путём добавления в services новых зависимостей.

  12. Метод BindConfiguration - позволяет настроить маппинг напрямую по названию секции в конфигурации.

  13. Запустите приложение с полным набором параметров:

    DOTNET_ENVIRONMENT=Development \
    AppSettings__Versions__0__Number=9 \
    dotnet run AppSettings:Versions:0:Number=10
    
  14. Вы получите вывод:

    Hello dotnet 10

    Можете поэкспериментировать и удостовериться что всё работает так же как в уроке 7.

<aside> 🐺 В этом уроке мы научились добавлять настройки приложения в DI. Это позволи нам получать доступ к настройкам в любой зависимости сервиса. Подробней про это можно почитать тут https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-7.0.

</aside>

<aside> 💡 Решение доступно тут https://github.com/vahpetr/lessions/tree/main/lesson10

</aside>