Получение зависимостей

Последнее обновление: 17.05.2022

Фреймворк ASP.NET Core позволяет получить все добавленные в приложение сервисы различными способами:

  • Через свойство Services объекта WebApplication

  • Через свойство RequestServices контекста запроса HttpContext в компонентах middleware

  • Через конструктор класса

  • Через параметр метода Invoke компонента middleware

  • Через свойство Services объекта WebApplicationBuilder

Для рассмотрения всех этих способов возьмем интерфейс ITimeService и класс ShortTimeService, который реализует данный интерфейс:

Interface ITimeService
    Function GetTime() As String
End Interface

Class ShortTimeService
    Implements ITimeService

    Public Function ITimeService_GetTime() As String Implements ITimeService.GetTime
        Return Now.ToShortTimeString()
    End Function
End Class

Свойство Services объекта WebApplication

Там, где нам доступен объект WebApplication, который представляет текущее приложение, (например, в файле Program.vb), для получения сервисов мы можем использовать его свойство Services. Это свойство предоставляет объект IServiceProvider, который предоставляет ряд методов для получения сервисов:

  • GetService(Of service)(): использует провайдер сервисов для создания объекта, который представляет тип service. В случае если в провайдере сервисов для данного сервиса не установлена зависимость, то возвращает значение null

  • GetRequiredService(Of service)(): использует провайдер сервисов для создания объекта, который представляет тип service. В случае если в провайдере сервисов для данного сервиса не установлена зависимость, то генерирует исключение

Данный паттерн получения сервиса еще называется service locator, и, как правило, не рекомендуется к использованию, но тем не менее в рамках ASP.NET Core в принципе мы можем использовать подобную функциональность особенно там, где другие способы получения зависимостей не достуны.

Например, определим в файле 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 ITimeService, ShortTimeService)

        Dim app = builder.Build()

        app.Run(Async Function(context As HttpContext) As Task
                    'получаем сервис 
                    Dim timeService = app.Services.GetService(Of ITimeService)()
                    Await context.Response.WriteAsync($"Time: {timeService?.GetTime}")
                End Function)
        app.Run()
    End Sub

    Interface ITimeService
        Function GetTime() As String
    End Interface

    Class ShortTimeService
        Implements ITimeService

        Public Function ITimeService_GetTime() As String Implements ITimeService.GetTime
            Return Now.ToShortTimeString()
        End Function
    End Class
End Module

В данном случае с помощью строки кода

Dim timeService = app.Services.GetService(Of ITimeService)(); 

Получаем из коллекции сервисов объект сервиса ITimeService - в данном случае он будет представлять объект ShortTimeService

Свойство Services в WebApplication и получение сервисов в ASP.NET Core и Visual Basic .NET

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

Sub Main(args As String())

    Dim builder = WebApplication.CreateBuilder(args)

    'builder.Services.AddTransient(Of ITimeService, ShortTimeService)

    Dim app = builder.Build()

    app.Run(Async Function(context As HttpContext) As Task
                Dim timeService = app.Services.GetService(Of ITimeService)()
                Await context.Response.WriteAsync($"Time: {timeService?.GetTime}")
            End Function)
    app.Run()
End Sub

В этом случае переменная timeService будет иметь значение Nothing.

Аналогичный образом можно использовать метод GetRequiredService() за тем исключением, что если сервис не добавлен, то метод генерирует исключение:

Sub Main(args As String())

    Dim builder = WebApplication.CreateBuilder(args)

    'builder.Services.AddTransient(Of ITimeService, ShortTimeService)

    Dim app = builder.Build()

    app.Run(Async Function(context As HttpContext) As Task
                Dim timeService = app.Services.GetRequiredService(Of ITimeService)()
                Await context.Response.WriteAsync($"Time: {timeService?.GetTime}")
            End Function)
    app.Run()
End Sub

HttpContext.RequestServices

Там, где нам доступен объект HttpContext, мы можем использовать для получения сервисов его свойство RequestServices. Это свойство предоставляет объект IServiceProvider. То есть по сути мы имеем дело с выше описанным способом получения сервисов с помощью методов GetService() и GetRequiredService():

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 ITimeService, ShortTimeService)

        Dim app = builder.Build()

        app.Run(Async Function(context As HttpContext) As Task
                    Dim timeService = context.RequestServices.GetService(Of ITimeService)()
                    Await context.Response.WriteAsync($"Time: {timeService?.GetTime}")
                End Function)
        app.Run()
    End Sub
    Interface ITimeService
        Function GetTime() As String
    End Interface
    Class ShortTimeService
        Implements ITimeService

        Public Function ITimeService_GetTime() As String Implements ITimeService.GetTime
            Return Now.ToShortTimeString()
        End Function
    End Class
End Module

Конструкторы

Встроенная в ASP.NET Core система внедрения зависимостей позволяет получить сервисы через конструкторы классов. Передача сервисов через конструкторы является предпочтительным способом внедрения зависимостей.

Например, изменим код файла следующим образом:

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 ITimeService, ShortTimeService)
        builder.Services.AddTransient(Of TimeMessage)

        Dim app = builder.Build()

        app.Run(Async Function(context As HttpContext) As Task
                    Dim timeMessenger = context.RequestServices.GetService(Of TimeMessage)()
                    context.Response.ContentType = "text/html;charset=utf-8"
                    Await context.Response.WriteAsync($"<h2>{timeMessenger?.GetTime()}</h2>")
                End Function)
        app.Run()
    End Sub

    Class TimeMessage
        Dim timeService As ITimeService
        Public Sub New(timeService As ITimeService)
            Me.timeService = timeService
        End Sub
        Public Function GetTime() As String
            Return $"Time:  {timeService.GetTime()}"
        End Function
    End Class

    Interface ITimeService
        Function GetTime() As String
    End Interface

    Class ShortTimeService
        Implements ITimeService

        Public Function ITimeService_GetTime() As String Implements ITimeService.GetTime
            Return Now.ToShortTimeString()
        End Function
    End Class
End Module
Внедрение зависимостей в конструкторы классов в ASP.NET Core и Visual Basic .NET

Здесь определен класс TimeMessage, который через конструктор получает зависимость от ITimeService:

Class TimeMessage
    Dim timeService As ITimeService
    Public Sub New(timeService As ITimeService)
        Me.timeService = timeService
    End Sub
    Public Function GetTime() As String
        Return $"Time:  {timeService.GetTime()}"
    End Function
End Class

Причем на момент создания класса неизвестно, что это будет за реализация интерфейса ITimeService. В методе GetTime() формируем сообщение, в котором из сервиса получаем текущее время.

Для использования в приложении в качестве сервиса класс MessageService также добавляется в коллекцию сервисов. Поскольку это самодостаточная зависимость, которая представляет конкретный класс, то метод builder.Services.AddTransient типизируется одним этим типом TimeMessage. То есть классы, которые используют сервисы, сами могут выступать в качестве сервисов.

builder.Services.AddTransient(Of TimeMessage)()

Но так как класс TimeMessage использует зависимость ITimeService, которая передается через конструктор, то нам надо также установить и эту зависимость:

builder.Services.AddTransient(Of ITimeService, ShortTimeService)()

И когда при обработке запроса будет использоваться класс TimeMessage, для создания объекта этого класса будет вызываться провайдер сервисов. Провайдер сервисов проверят конструктор класса TimeMessage на наличие зависимостей. Затем создает объекты для всех используемых зависимостей и передает их в конструктор.

Метод Invoke/InvokeAsync компонентов middleware

Подобно тому, как зависимости передаются в конструктор классов, точно также их можно передавать в метод Invoke/InvokeAsync() компонента middleware. Например,определим следующий компонент:

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 ITimeService, ShortTimeService)

        Dim app = builder.Build()

        app.UseMiddleware(Of TimeMessageMiddleware)
        app.Run()
    End Sub

    Class TimeMessageMiddleware

        Public Sub New(nextMiddleware As RequestDelegate)
        End Sub

        Public Async Function InvokeAsync(context As HttpContext, timeService As ITimeService) As Task
            context.Response.ContentType = "text/html;charset=utf-8"
            Await context.Response.WriteAsync($"<h1>Time: {timeService.GetTime()}</h1>")
        End Function

    End Class

    Interface ITimeService
        Function GetTime() As String
    End Interface

    Class ShortTimeService
        Implements ITimeService

        Public Function ITimeService_GetTime() As String Implements ITimeService.GetTime
            Return Now.ToShortTimeString()
        End Function
    End Class
End Module

Здесь определен класс-middleware TimeMessageMiddleware, который в методе InvolkeAsync получает сервис ITiemService и использует его:

Class TimeMessageMiddleware

    Public Sub New(nextMiddleware As RequestDelegate)
    End Sub

    Public Async Function InvokeAsync(context As HttpContext, timeService As ITimeService) As Task
        context.Response.ContentType = "text/html;charset=utf-8"
        Await context.Response.WriteAsync($"<h1>Time: {timeService.GetTime()}</h1>")
    End Function

End Class

Стоит отметить, что мы также могли бы передать зависимость и через конструктор класса middleware:

Class TimeMessageMiddleware

    Dim timeService As ITimeService
    Public Sub New(nextMiddleware As RequestDelegate, timeService As ITimeService)
        Me.timeService = timeService
    End Sub

    Public Async Function InvokeAsync(context As HttpContext) As Task
        context.Response.ContentType = "text/html;charset=utf-8"
        Await context.Response.WriteAsync($"

Time: {timeService.GetTime()}

") End Function End Class

Благодаря подобной передаче зависимости мы можем уйти от использования паттерна "service locator", который демонстрировался в предыдущих примерах.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850