Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Через конструктор класса (за исключением конструктора класса Startup)
Через параметр метода Configure класса Startup
Через параметр метода Invoke компонента middleware
Через свойство RequestServices контекста запроса HttpContext в компонентах middleware (service locator)
Через свойство ApplicationServices объекта IApplicationBuilder в классе Startup
Для работы возьмем проект ASP.NET Core по типу Empty и определим в нем типы IMessageSender и EmailMessageSender:
public interface IMessageSender { string Send(); } public class EmailMessageSender : IMessageSender { public string Send() { return "Sent by Email"; } }
Метод Configure класса Startup позволяет напрямую получать зависимость в качестве параметра метода. Например:
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<IMessageSender, EmailMessageSender>(); } // Получаем зависимость IMessageSender public void Configure(IApplicationBuilder app, IMessageSender sender) { app.Run(async (context) => { await context.Response.WriteAsync(sender.Send()); }); } }
Данный способ естественно может применяться только в классе Startup.
Встроенная в ASP.NET Core система внедрения зависимостей использует конструкторы классов для передачи всех зависимостей. Соответственно в конструкторе контроллера мы можем получить зависимость. Конструкторы являются наиболее предпочтительным вариантом для получения зависимостей.
Например, пусть в проекте определен класс MessageService:
public class MessageService { IMessageSender _sender; public MessageService(IMessageSender sender) { _sender = sender; } public string Send() { return _sender.Send(); } }
Здесь через конструктор класса передается зависимость от IMessageSender. Причем здесь неизвестно, что это будет за реализация интерфейса IMessageSender.
Изменим класс Startup для использования MessageService:
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<IMessageSender, EmailMessageSender>(); services.AddTransient<MessageService>(); } public void Configure(IApplicationBuilder app, MessageService messageService) { app.Run(async (context) => { await context.Response.WriteAsync(messageService.Send()); }); } }
Чтобы использовать класс MessageService, он внедряется в приложение в виде сервиса. Поскольку это самодостаточная зависимость, которая представляет
конкретный класс, то метод services.AddTransient
типизируется одним этим типом MessageService. То есть классы, использующие сервисы, сами
могут выступать в качестве сервисов.
Но так как класс MessageService использует зависимость IMessageSender, которая передается через конструктор, то нам надо также установить и эту зависимость:
services.AddTransient<IMessageSender, EmailMessageSender>();
И когда при обработке запроса будет использоваться класс MessageService, для создания объекта этого класса будет вызываться провайдер сервисов. Провайдер сервисов проверят конструктор класса MessageService на наличие зависимостей. Затем создает объекты для всех используемых зависимостей и передает их в конструктор.
В методе Configure сервис MessageService передается в качестве параметра и участвует в обработке запроса.
А результатом работы приложения будет вывод строки:
Данный способ может применяться для передачи зависимостей в конструктор любого класса кроме класса Startup.
Объект HttpContext.RequestServices предоставляет доступ к всем внедренным зависимостям с помощью своих методов:
GetService<service>(): использует провайдер сервисов для создания объекта, который представляет тип service. В случае если в провайдере сервисов для данного сервиса не установлена зависимость, то возвращает значение null
GetRequiredService<service>(): использует провайдер сервисов для создания объекта, который представляет тип service. В случае если в провайдере сервисов для данного сервиса не установлена зависимость, то генерирует исключение
Данный паттерн получения сервиса еще называется service locator, и, как правило, не рекомендуется к использованию, но тем не менее в рамках ASP.NET Core в принципе мы можем использовать подобную функциональность.
Например, изменим класс Startup:
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<IMessageSender, EmailMessageSender>(); } public void Configure(IApplicationBuilder app) { app.Run(async (context) => { IMessageSender messageSender = context.RequestServices.GetService<IMessageSender>(); context.Response.ContentType = "text/html;charset=utf-8"; await context.Response.WriteAsync(messageSender.Send()); }); } }
Еще один похожий способ представляет получение сервисов через свойство ApplicationServices объекта IApplicationBuilder, который передается в качестве параметра в метод Configure класса Startup:
public void Configure(IApplicationBuilder app) { app.Run(async (context) => { IMessageSender messageSender = app.ApplicationServices.GetService<IMessageSender>(); context.Response.ContentType = "text/html;charset=utf-8"; await context.Response.WriteAsync(messageSender.Send()); }); }
Подобно тому, как зависимости передаются в метод Configure в классе Startup, точно также их можно передавать в метод Invoke компонента middleware. Например,определим следующий компонент:
using Microsoft.AspNetCore.Http; using System.Threading.Tasks; public class MessageMiddleware { private readonly RequestDelegate _next; public MessageMiddleware(RequestDelegate next) { this._next = next; } public async Task InvokeAsync(HttpContext context, IMessageSender messageSender) { context.Response.ContentType = "text/html;charset=utf-8"; await context.Response.WriteAsync(messageSender.Send()); } }
Применим компонент для обработки запроса в классе Startup:
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<IMessageSender, EmailMessageSender>(); } public void Configure(IApplicationBuilder app) { app.UseMiddleware<MessageMiddleware>(); } }