Встроенный механизм внедрения зависимостей в ASP.NET Core позволяет управлять жизненным циклом внедряемых в приложении сервисов. С точки зрения жизненного цикла сервисы могут представлять один из следующих типов:
Transient: при каждом обращении к сервису создается новый объект сервиса. В течение одного запроса может быть несколько обращений к сервису, соответственно при каждом обращении будет создаваться новый объект. Подобная модель жизненного цикла наиболее подходит для легковесных сервисов, которые не хранят данных о состоянии
Scoped: для каждого запроса создается свой объект сервиса. То есть если в течение одного запроса есть несколько обращений к одному сервису, то при всех этих обращениях будет использоваться один и тот же объект сервиса.
Singleton: объект сервиса создается при первом обращении к нему, все последующие запросы используют один и тот же ранее созданный объект сервиса
Для создания каждого типа сервиса предназначен соответствующий метод AddTransient(), AddScoped() и AddSingleton().
Для рассмотрения механизма внедрения зависимостей и жизненного цикла возьмем следующий интерфейс ICounter:
Public Interface ICounter ReadOnly Property Number As Integer End Interface
Также определим реализацию этого интерфейса - класс RandomCounter
Class RandomCounter Implements ICounter Shared rnd = New Random() Dim _number As Integer Public Sub New() _number = rnd.Next(0, 1000000) End Sub Public ReadOnly Property Number As Integer Implements ICounter.Number Get Return _number End Get End Property End Class
Суть класса RandomCounter состоит в генерации некоторого случайного числа в диапазоне от 0 до 1000000.
И также определим новый класс, который нам более детально поможет разобраться в механизме Depedency Injection. Этот класс назовем CounterService и определим в нем следующий код:
Public Class CounterService Public ReadOnly Property Counter As ICounter Public Sub New(counter As ICounter) Me.Counter = counter End Sub End Class
Данный класс просто устанавливает объект ICounter, передаваемый через конструктор.
Для работы с сервисами определим компонент middleware, который назовем CounterMiddleware:
Imports Microsoft.AspNetCore.Http Public Class CounterMiddleware Dim i = 0 'счетчик запросов Public Sub New(nextMiddleware As RequestDelegate) End Sub Public Async Function InvokeAsync(httpContext As HttpContext, counter As ICounter, countService As CounterService) As Task i += 1 httpContext.Response.ContentType = "text/html;charset=utf-8" Await httpContext.Response.WriteAsync($"Запрос {i}; Counter: {counter.Number}; Service: {countService.Counter.Number}") End Function End Class
Для получения зависимостей здесь используется метод InvokeAsync, в котором передаются две зависимости ICounter и CounterService. В самом методе выводятся значения Value из обоих зависимостей. Причем сервис CounterService сам использует зависимость ICounter.
То есть структура проекта будет выглядеть следующим образом:
Теперь на примере этих классов рассмотрим уравление жизненным циклом сервисов.
Метод AddTransient()
создает transient-объекты. Такие объекты создаются при каждом обращении к ним. Данный метод имеет ряд перегруженных версий:
AddTransient(serviceType As Type)
AddTransient(serviceType As Type, implementationType As Type)
AddTransient(serviceType As Type, Func(Of IServiceProvider, Object) implementationFactory)
AddTransient(Of TService)()
AddTransient(Of TService, TImplementation)()
AddTransient(Of TService)(Func(Of IServiceProvider,TService) implementationFactory)
AddTransient(Of TService, TImplementation)(Func(Of IServiceProvider, TImplementation) implementationFactory)
Используем данный метод для добавления сервисов в файле Program.vb:
Imports Microsoft.AspNetCore.Builder Imports Microsoft.Extensions.DependencyInjection Module Program Sub Main(args As String()) Dim builder = WebApplication.CreateBuilder(args) builder.Services.AddTransient(Of ICounter, RandomCounter)() builder.Services.AddTransient(Of CounterService)() Dim app = builder.Build() app.UseMiddleware(Of CounterMiddleware)() app.Run() End Sub End Module
Запустим проект:
В нашем случае CounterMiddleware получает объект ICounter, для которого создается один экземпляр класса RandomCounter. CounterMiddleware также получает объект CounterService, который также использует ICounter. И для этого ICounter будет создаваться второй экземпляр класса RandomCounter. Поэтому генерируемые случайные числа обоими экземплярами не совпадают. Таким образом, применение AddTransient создаст два разных объекта RandomCounter.
При втором и последующих запросах к контроллеру будут создаваться новые объекты RandomCounter.
Метод AddScoped создает один экземпляр объекта для всего запроса. Он имеет те же перегруженные версии, что и AddTransient. Для его применения изменим код приложения следующим образом:
Imports Microsoft.AspNetCore.Builder Imports Microsoft.Extensions.DependencyInjection Module Program Sub Main(args As String()) Dim builder = WebApplication.CreateBuilder(args) builder.Services.AddScoped(Of ICounter, RandomCounter)() builder.Services.AddScoped(Of CounterService)() Dim app = builder.Build() app.UseMiddleware(Of CounterMiddleware)() app.Run() End Sub End Module
Теперь в рамках одного и того же запроса и CounterMiddleware и сервис CounterService будут использовать один и тот же объект RandomCounter. При следующем запросе к приложению будет генерироваться новый объект RandomCounter.
AddSingleton создает один объект для всех последующих запросов, при этом объект создается только тогда, когда он непосредственно необходим. Этот метод имеет все те же перегруженые версии, что и AddTransient и AddScoped.
Для применения AddSingleton изменим код приложения:
Imports Microsoft.AspNetCore.Builder Imports Microsoft.Extensions.DependencyInjection Module Program Sub Main(args As String()) Dim builder = WebApplication.CreateBuilder(args) builder.Services.AddSingleton(Of ICounter, RandomCounter)() builder.Services.AddSingleton(Of CounterService)() Dim app = builder.Build() app.UseMiddleware(Of CounterMiddleware)() app.Run() End Sub End Module
Как можно заметить, все три метода внедрения зависимостей имеют один и тот же принцип использования, различается только название метода. И в этом случае при нескольких последовательных запросах мы получим следующий результат: