Как можно зарегистрировать реализацию по умолчанию с помощью контейнера 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 { }
}
Встроенный .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
@ShahzadHassan: Как я интерпретирую вопрос ОП, замена Default
— это именно то, что он хочет.
Это означает, что Default
никогда не будет использоваться, так какой смысл регистрировать Default
?
@ShahzadHassan: Первая регистрация производится пакетом, вторая регистрация по выбору производится разработчиком приложения, использующим пакет, но только в случае, если он хочет заменить Default
.
Если ваш пакет содержит класс, который настраивает 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
, вы можете разрешить ему указывать только тип службы, а ваша конфигурация определяет, как он будет зарегистрирован (одноэлементный, переходный и т. д.).
Это также полезный шаблон, когда библиотека зависит от значений конфигурации, таких как строки подключения, которые должны быть предоставлены потребителем. Вы можете сделать их обязательными аргументами для построения параметров, а затем потребовать эти параметры для построения установщика, или просто сделать их обязательными аргументами в установщике. Теперь невозможно установить пакет без необходимых значений конфигурации.
Вы можете зарегистрировать любой сервис внутри своего пакета и предоставлять его через интерфейсы. Затем, когда вы будете использовать его в каком-то проекте, все, что вам нужно сделать, чтобы переопределить реализацию пакета по умолчанию, — это переопределить один из открытых интерфейсов, и это все.
В вашей регистрации: docs.microsoft.com/en-us/dotnet/api/… ?