После добавления сервисов в коллекцию Services объекта WebApplicationBuilder они становятся доступны приложению, в том числе и в кастомных компонентах middleware. В middleware мы можем получить зависимости тремя способами:
Через конструктор
Через параметр метода Invoke/InvokeAsync
Через свойство HttpContext.RequestServices
При этом надо учитывать, что компоненты middleware создаются при запуске приложения и живут в течение всего жизненного цикла приложения. То есть при последующих запросах инфраструктура ASP.NET Core использует ранее созданный компонент. И это налагает ограничения на использование зависимостей в middleware.
В частности, если конструктор передается transient-сервис, который создается при каждом обращении к нему, то при последующих запросах мы будем использовать тот же самый сервис, так как конструктор middleware вызывается один раз - при создании приложения.
Например, определим сервис TimeService:
Public Class TimeService Public ReadOnly Property Time = Now.ToLongTimeString() End Class
Его свойство Time получает текущее время в виде строки.
Добавим новый компонент TimerMiddleware, который будет использовать этот сервис для вывода времени на веб-страницу:
Imports Microsoft.AspNetCore.Http Public Class TimerMiddleware Dim service As TimeService Dim nextMiddleware As RequestDelegate Public Sub New(nextMiddleware As RequestDelegate, service As TimeService) Me.service = service Me.nextMiddleware = nextMiddleware End Sub Public Async Function InvokeAsync(context As HttpContext) As Task If context.Request.Path.Value = "/time" Then Await context.Response.WriteAsync($"Текущее время: {service.Time}") Else Await nextMiddleware.Invoke(context) End If End Function End Class
Если сервис TimeService добавляется в коллекцию сервисов приложения, то мы сможем получить его через конструктор класса TimerMiddleware.
Логика компонента предполагает, что, если запрос пришел по адресу "/time", то с помощью TimeService возвращается текущее время. Иначе мы просто обращаемся к следующему middleware в конвейере обработки запроса.
Используем этот компонент в файле Program.vb:
Imports Microsoft.AspNetCore.Builder Imports Microsoft.AspNetCore.Http Imports Microsoft.Extensions.DependencyInjection Module Program Sub Main(args As String()) Dim builder = WebApplication.CreateBuilder(args) builder.Services.AddTransient(Of TimeService)() Dim app = builder.Build() app.UseMiddleware(Of TimerMiddleware)() app.Run(Async Function(context As HttpContext) As Task Await context.Response.WriteAsync("Hello METANIT.COM") End Function) app.Run() End Sub End Module
В итоге, если мы обратимся по пути "/time", то приложение выведет текущее время:
Однако сколько бы мы раз не обращались по этому пути, мы все время будем получать одно и то же время, так как объект TimerMiddleware был создан еще при первом запросе. Поэтому передача через конструктор middleware больше подходит для сервисов с жизненным циклом Singleton, которые создаются один раз для всех последующих запросов.
Если же в middleware необходимо использовать сервисы с жизненным циклом Scoped или Transient, то лучше их передавать через параметр метода Invoke/InvokeAsync:
Imports Microsoft.AspNetCore.Http Public Class TimerMiddleware Dim nextMiddleware As RequestDelegate Public Sub New(nextMiddleware As RequestDelegate) Me.nextMiddleware = nextMiddleware End Sub Public Async Function InvokeAsync(context As HttpContext, service As TimeService) As Task If context.Request.Path.Value = "/time" Then Await context.Response.WriteAsync($"Текущее время: {service.Time}") Else Await nextMiddleware.Invoke(context) End If End Function End Class