Совместное использование конфигурации Microsoft.Extensions.DependencyInjection.ServiceProvider между проектами. - PullRequest
0 голосов
/ 28 июня 2018

У меня есть решение, которое имеет следующие проекты

  • Acme.Core
  • Acme.Domain
  • Acme.Repositories
  • Acme.Services
  • Acme.Web

В прошлом я использовал Unity для DI в проектах с полной структурой. Мне удалось зарегистрировать конкретные объекты для сопоставления интерфейсов в исполняемых проектах (веб-приложения, консольное приложение, тестовые приложения).

Я пытаюсь реализовать тот же подход с .NET Core. Я хотел сначала попытаться использовать библиотеку Microsoft.Extensions.DependencyInjection. В приложении ASP.NET Core это прекрасно работает. К сожалению, у меня возникла проблема, когда я пытаюсь поделиться / сослаться на этот экземпляр с регистрациями в других проектах, таких как библиотека .NET Standard.

Моя идея заключалась в том, чтобы внедрить ServiceProvider в конструктор службы:

public class AddressService : BaseService, IAddressService
{
        private readonly IServiceProvider _serviceProvider;

        public AddressService(IServiceProvider serviceProvider, string userOrProcessName)
        {
           _serviceProvider = serviceProvider;
        } 

        public IReadOnlyList<IState> GetAllStates()
        {
            _serviceProvider.GetService<IAddressRepository>();

            // other logic removed
        }
}

Я попробовал следующее в Startup.ConfigureServices ():

services.AddTransient<IAddressService>(s => new AddressService(HttpContext.RequestServices, Environment.UserName));

Проблема, с которой я столкнулся, заключается в том, что я не могу ссылаться на HttpContext.RequestServices за пределами контроллера. Я не смог найти другой способ передачи экземпляра ServiceProvider.

Мои вопросы:

  1. Как передать ссылку для текущего ServiceProvider?
  2. Есть ли лучший дизайн для достижения моей цели, разделяющий конфигурацию Microsoft.Extensions.DependencyInjection в нескольких библиотеках?

Ответы [ 2 ]

0 голосов
/ 28 июня 2018

Чтобы разделить конфигурацию между несколькими проектами, вы можете поместить конфигурацию в общую сборку и зарегистрировать (не разрешить) ее там. Многие библиотеки внедрения зависимостей предлагают эту функциональность. например

На высоком уровне вы создаете метод, который получает контейнер DI и добавляет ваши регистрации в этот контейнер. Если ваша структура DI не предоставляет хуков, вам нужно вручную вызвать метод, но общая концепция не изменится.

Разделение регистраций на модули позволяет легко группировать схожие наборы функций, сохраняя при этом гибкость включения различных наборов функций в разные проекты. Конечно, вы могли бы создать единую общую сборку, в которой было бы зарегистрировано объединение всех зависимостей для всех проектов, но это повлекло бы за собой ненужный багаж и приводило бы к менее повторяемой реализации.

Ключевой момент, на который указывает Стивен, заключается в том, что вы настраиваете контейнер и позволяете ему внедрять зависимости, а не просматриваете их изнутри.

0 голосов
/ 28 июня 2018

Запретить ввод IServiceProvider в компоненты вашего приложения; это приводит к анти-шаблону Service Locator .

Вместо этого вы должны создавать компоненты приложения исключительно с использованием Конструктор Инъекции . Это означает, что ваш AddressService должен требовать IAddressRepository в качестве аргумента конструктора, а не IServiceProvider. Например:

public class AddressService : IAddressService
{
    private readonly IAddressRepository repo;

    public AddressService(IAddressRepository repo, IUserContext userContext)
    {
       this.repo = repo;
    } 

    public IReadOnlyList<IState> GetAllStates()
    {
        // other logic removed
    }
}

Также попытайтесь предотвратить внедрение примитивов в ваши конструкторы. Это не плохая практика как таковая, но она усложняет построение графов объектов. Вместо этого либо оберните значение в класс, если оно является значением конфигурации, либо скройте его за абстракцией (как показано выше), если это значение времени выполнения.

Обе практики упрощают как код приложения, так и корень композиции .

Например, это будет результатом предыдущего AddressService редизайна:

services.AddTransient<IAddressRepository, SqlAddressRepository>();
services.AddTransient<IAddressService, AddressService>();
services.AddScoped<IUserContext, UserContext>();
services.AddHttpContextAccessor();

Здесь UserContext можно определить следующим образом:

public class UserContext : IUserContext
{
    private readonly IHttpContextAccessor accessor;
    public UserContext(IHttpContextAccessor accessor) => this.accessor = accessor;
    public string UserName => this.accessor.HttpContext.User.Identity.Name;
}
...