Регистрация сервиса

В этой статье описывается регистрация групп служб и служб, предоставляемых платформой. Он также содержит детали о методах расширения регистрации сервисов, которые предоставляет .NET.

Регистрация групп сервисов с помощью методов расширения

.NET использует соглашение для регистрации группы связанных служб. Соглашение заключается в использовании одного метода расширения Add{GROUP_NAME} для регистрации всех служб, необходимых компоненту платформы. Например, метод расширения AddOptions регистрирует все службы, необходимые для работы с параметрами.

Службы, предоставляемые фреймворком

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

После создания построителя из любого из этих API в системе есть службы, определяемые платформой, в зависимости от того, IServiceCollectionкак вы настроили узел. Для приложений на основе шаблонов .NET платформа может зарегистрировать сотни служб.

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

тип услуги; Срок службы
Microsoft.Extensions.DependencyInjection.IServiceScopeFactory Singleton
IHostApplicationLifetime Singleton
Microsoft.Extensions.Logging.ILogger<TCategoryName> Singleton
Microsoft.Extensions.Logging.ILoggerFactory Singleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider Singleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> Transient
Microsoft.Extensions.Options.IOptions<TOptions> Singleton
System.Diagnostics.DiagnosticListener Singleton
System.Diagnostics.DiagnosticSource Singleton

Методы регистрации

Платформа предоставляет методы расширения регистрации служб, которые полезны в определенных сценариях:

Метод Автоматическое удаление объектов Несколько реализаций Передача аргументов
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()

Пример:

services.AddSingleton<IMyDep, MyDep>();
Да Да нет
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})

Примеры.

services.AddSingleton<IMyDep>(sp => new MyDep());
services.AddSingleton<IMyDep>(sp => new MyDep(99));
Да Да Да
Add{LIFETIME}<{IMPLEMENTATION}>()

Пример:

services.AddSingleton<MyDep>();
Да нет нет
AddSingleton<{SERVICE}>(new {IMPLEMENTATION})

Примеры.

services.AddSingleton<IMyDep>(new MyDep());
services.AddSingleton<IMyDep>(new MyDep(99));
нет Да Да
AddSingleton(new {IMPLEMENTATION})

Примеры.

services.AddSingleton(new MyDep());
services.AddSingleton(new MyDep(99));
нет нет Да

Дополнительные сведения об удалении типов см. в разделе "Удаление служб".

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

services.AddSingleton<ExampleService>();

Это эквивалентно регистрации сервиса с указанием как самого сервиса, так и его реализации одного и того же типа.

services.AddSingleton<ExampleService, ExampleService>();

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

Любой из методов регистрации службы можно использовать для регистрации нескольких экземпляров службы одного типа службы. В следующем примере метод AddSingleton вызывается дважды с типом службы IMessageWriter. Второй вызов AddSingleton переопределяет предыдущий, если он разрешается как IMessageWriter, и добавляет к предыдущему, если несколько служб разрешаются через IEnumerable<IMessageWriter>. Службы отображаются в том порядке, в котором они были зарегистрированы при разрешении через IEnumerable<{SERVICE}>.

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

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();
builder.Services.AddSingleton<ExampleService>();

using IHost host = builder.Build();

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

await host.RunAsync();

Предыдущий пример исходного кода регистрирует две реализации IMessageWriter.

using System.Diagnostics;

namespace ConsoleDI.IEnumerableExample;

public sealed class ExampleService
{
    public ExampleService(
        IMessageWriter messageWriter,
        IEnumerable<IMessageWriter> messageWriters)
    {
        Trace.Assert(messageWriter is LoggingMessageWriter);

        var dependencyArray = messageWriters.ToArray();
        Trace.Assert(dependencyArray[0] is ConsoleMessageWriter);
        Trace.Assert(dependencyArray[1] is LoggingMessageWriter);
    }
}

Компонент ExampleService определяет два параметра конструктора: одиночный IMessageWriter и IEnumerable<IMessageWriter>. Единственный IMessageWriter является последней реализацией, которая зарегистрирована, в то время как IEnumerable<IMessageWriter> представляет все зарегистрированные реализации.

Платформа также предоставляет методы расширения TryAdd{LIFETIME}, которые регистрируют службу только в том случае, если реализация еще не зарегистрирована.

В следующем примере вызов AddSingleton регистрирует ConsoleMessageWriter как реализацию для IMessageWriter. Вызов TryAddSingleton ничего не делает, поскольку у IMessageWriter уже есть зарегистрированная реализация:

services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
services.TryAddSingleton<IMessageWriter, LoggingMessageWriter>();

TryAddSingleton не оказывает эффекта, так как оно уже было добавлено, и "try" завершается неудачей. ExampleService утверждает следующее:

public class ExampleService
{
    public ExampleService(
        IMessageWriter messageWriter,
        IEnumerable<IMessageWriter> messageWriters)
    {
        Trace.Assert(messageWriter is ConsoleMessageWriter);
        Trace.Assert(messageWriters.Single() is ConsoleMessageWriter);
    }
}

Дополнительные сведения можно найти здесь

Методы TryAddEnumerable(ServiceDescriptor) регистрируют службу только в том случае, если еще не существует реализации того же типа. Несколько служб обрабатывается через IEnumerable<{SERVICE}>. При регистрации служб добавьте экземпляр, если один из тех же типов еще не добавлен. Авторы библиотек используют TryAddEnumerable, чтобы избежать регистрации нескольких копий реализации в контейнере.

В следующем примере первый вызов TryAddEnumerable регистрирует MessageWriter как реализацию для IMessageWriter1. Второй вызов регистрирует MessageWriter для IMessageWriter2. Третий вызов ничего не делает, поскольку у IMessageWriter1 уже есть зарегистрированная реализация MessageWriter:

public interface IMessageWriter1 { }
public interface IMessageWriter2 { }

public class MessageWriter : IMessageWriter1, IMessageWriter2
{
}

services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1, MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter2, MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1, MessageWriter>());

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

IServiceCollection является коллекцией объектов ServiceDescriptor. В следующем примере показано, как зарегистрировать службу, создав и добавив ServiceDescriptor:

string secretKey = Configuration["SecretKey"];
var descriptor = new ServiceDescriptor(
    typeof(IMessageWriter),
    _ => new DefaultMessageWriter(secretKey),
    ServiceLifetime.Transient);

services.Add(descriptor);

Встроенные методы Add{LIFETIME} используют аналогичный подход. Например, см. исходный код для AddScoped.