.NET Core DI, зарегистрируйте реализацию по умолчанию для пакета

Как можно зарегистрировать реализацию по умолчанию с помощью контейнера IoC для .NET Core, а также предоставить способ переопределить существующую реализацию?

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

namesapce Package 
{
    public interface ISomeService { }

    public class Default : ISomeService { }
}

Затем этот сервис используется внутри того же пакета.

namesapce Package 
{
    public class Service 
    {
        Service(ISomeService service) { }
    }
}

Как зарегистрировать реализацию ISomeService по умолчанию?

Позже, когда вы используете этот пакет в каком-либо проекте и хотите переопределить существующую реализацию другой, значение по умолчанию следует заменить на «Переопределить».

namespace Project 
{
    public class Override : ISomeService { }
}

В вашей регистрации: docs.microsoft.com/en-us/dotnet/api/… ?

Tseng 22.05.2019 13:32
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
1
1
1 047
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Встроенный .NET Core DI Container позволяет разработчику приложения переопределить регистрацию вашего пакета в ServiceCollection, просто добавив ту же службу в ServiceCollection. Если для одного типа службы сделано несколько регистраций, будет использована последняя регистрация. Например:

// Package registrations (part of your Package)
services.AddTransient<ISomeService, Default>();

// Override by application developer (part of his Startup.cs)
services.AddTransient<ISomeService, Override>();

РАССМОТРИТЕ для создания пакета таким образом, чтобы он не требовал использования контейнера внедрения зависимостей, как описано Марком Симаном в его статье DI-дружественная библиотека.

OP просит переопределить существующую реализацию позже, когда это необходимо. С предложенным вами решением будет доступно только Override в качестве реализации по умолчанию ISomeService

Shahzad Hassan 22.05.2019 16:24

@ShahzadHassan: Как я интерпретирую вопрос ОП, замена Default — это именно то, что он хочет.

Steven 22.05.2019 16:28

Это означает, что Default никогда не будет использоваться, так какой смысл регистрировать Default?

Shahzad Hassan 22.05.2019 17:08

@ShahzadHassan: Первая регистрация производится пакетом, вторая регистрация по выбору производится разработчиком приложения, использующим пакет, но только в случае, если он хочет заменить Default.

Steven 22.05.2019 17:09
Ответ принят как подходящий

Если ваш пакет содержит класс, который настраивает IServiceCollection, например:

public class MyPackageInstaller
{
    public void Install(IServiceCollection services)
    {
        // Your package registers its services
    }
}

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

public class MyPackageRegistrationOptions
{
    public ServiceDescriptor FooServiceDescriptor { get; private set; }

    public void AddFooService(ServiceDescriptor fooDescriptor)
    {
        if (fooDescriptor.ServiceType != typeof(IFooService))
        {
            throw new ArgumentException("fooDescriptor must register type IFooService.");
        }
        FooServiceDescriptor = fooDescriptor;
    }
}

Теперь ваш установщик может использовать эти параметры и зарегистрировать либо реализацию, указанную потребителем, либо собственную реализацию по умолчанию.

public class MyPackageInstaller
{
    private readonly MyPackageRegistrationOptions _options;

    public MyPackageInstaller(MyPackageRegistrationOptions options = null)
    {
        _options = options;
    }
    public void Install(IServiceCollection services)
    {
        if (_options?.FooServiceDescriptor != null)
            services.Add(_options.FooServiceDescriptor);
        else 
             // here's your default implementation
            services.AddSingleton<FooService>();
    }
}

Применение:

var services = new ServiceCollection();
var options = new MyPackageRegistrationOptions();
options.AddFooService(ServiceDescriptor.Singleton<IFooService, AlternateFooService>());
var installer = new MyPackageInstaller(options);
installer.Install(services);

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

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

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

Вы можете зарегистрировать любой сервис внутри своего пакета и предоставлять его через интерфейсы. Затем, когда вы будете использовать его в каком-то проекте, все, что вам нужно сделать, чтобы переопределить реализацию пакета по умолчанию, — это переопределить один из открытых интерфейсов, и это все.

Другие вопросы по теме