company_banner

Готовим ASP.NET5, выпуск №3 — внедрение зависимостей по-новому

    Мы продолжаем нашу колонку по теме ASP.NET5 публикацией от Виктора Коцюбана ( Gbdrm) — Technical Leader из SoftServe. В этой статье Виктор поделится с вами подробностями нового встроенного функционала внедрений зависимостей в ASP.NET5. Предыдущие статьи из колонки всегда можно прочитать по ссылке #aspnetcolumn — Владимир Юнев
    Внедрение зависимости – одна из самых популярных и используемых форм инверсии управления, важного принципа ООП, что позволяет уменьшить сцепление (coupling) – взаимозависимость, взаимосвязанность модулей.

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

    В ASP.NET 5 встроена возможность внедрения зависимости. Что она позволяет и чем выделяется среди других таких подходов рассмотрим ниже. А также попробуем использовать ее на практике.

    ASP.NET 5 находится в бета версии и все что здесь описано может изменится до финального релиза. Например, не так давно были серьезные изменения в возможностях внедрения в свойства. Но основная концепция и главные направления этого нововведения уже понятны.

    Основы и регистрация сервисов


    Большинство зависимостей, что нужны для представлений, контроллеров и т.д. реализованы как сервисы, потому сервис – это минимальная единица которой управляет DI контейнер. Ключевые возможности минималистического контейнера ASP.NET 5 вынесены в интерфейс IServiceProvider. Единственный метод этого интерфейса – object GetService(Type serviceType).

    Есть 4 типа инициализации сервисов (4 типа области видимости), которые поддерживаются контейнером:
    1. Instance – возвращается конкретный объект, за создание которого отвечаете вы сами.
    2. Transient – каждый раз возвращается новый объект.
    3. Singleton – всегда возвращается один и тот же объект.
    4. Scoped – эквивалентен сиглтону, но в данной области (например, в области запроса).

    Регистрируются сервисы на старте приложения, в классе Startup, метод ConfigureServices(IServiceCollection services), при чем коллекция ServiceCollection так же имеет методы для добавления сервиса с соответствующей областью видимости (Рис.1).


    Рис.1. — Диаграмма классов

    Пример использования extension-методов коллекции ServiceCollection:

    public virtual void ConfigureServices(IServiceCollection services)
    {
        var settings = new Settings();
        services.AddInstance(settings);
    
        services.AddScoped<IProductService, ProductService>();
    
        services.AddSingleton<TestService>();
    }

    После регистрации сервисы доступны во всех классах, что были вызваны через DI, а также доступных в представлениях.

    Внедряем сервисы в контроллер


    С внедрением в контроллеры все очень просто – оно происходит через конструктор контроллера.

    public class HomeController : Controller
    {
        private readonly IApplicationEnvironment _appEnvironment;
        private readonly IProductService _productService;
     
        public HomeController(IProductService productService, 
                              IApplicationEnvironment appEnvironment)
        {
            _appEnvironment = appEnvironment;
            _productService = productService;
        }
     
        // ...
    }

    И больше ничего делать не нужно. Зарегистрированные сервисы доступны как параметры конструктора.
    Если мы используем EntityFramework в нашем приложении, то одна их первых зависимостей, о которых мы задумаемся для использования DI – это DbContext. Это позволит нам написать менее завязанный на DbContext код, легче тестировать такой функционал и т.д.

    Пример:
    github.com/gbdrm/aspnet5/blob/master/src/aspnet5/Controllers/HomeController.cs#L12-L17

    Внедрение в представления


    Так же существует интересная возможность использования зарегистрированных сервисов в представлениях (*.cshtml).

    @model SomeViewModel
    @inject SomeContext SomeContext
    
    <h1>@SomeContext.PageTitle</h1>
     
        <!-- HTML -->

    Директива @inject говорит движку представлений, что мы хотим использовать сервис SomeContext. И в самом представлении уже можно использовать этот сервис, в данном случае – свойство PageTitle.

    Кроме использования сервисов в представлениях еще одно интересное нововведение – это типизированные настройки. Кроме регистрации сервисов есть возможность «зарегистрировать» часть конфигурации, перед этим приведя эту конфигурацию к определенному типу и сделать это можно всего в несколько строк кода.
    Пример внедрения типизированной конфигурации в представлении:
    github.com/gbdrm/aspnet5/commit/88dc1e708f89edfd30a55ae90265cde9074ae312

    Внедрение в свойства и куда пропал [Activate]


    Раньше с помощью атрибута [Activate] можно было вытащить соответствующий сервис для свойства. Но разработчиками было принято решение отказаться от этого. Главными причинами для этого послужили проблемы с совместимостью с другими библиотеками для внедрения зависимостей, а также усложнение отладки. Некоторое время проходили дискуссии по этому поводу, много деталей можно прочитать здесь: github.com/aspnet/Mvc/issues/2151, github.com/aspnet/Announcements/issues/28

    Но все же возможность внедрения зависимостей через свойства осталась – с помощью атрибута [FromServices].

    Давайте создадим тестовый класс, добавим его в контейнер и попробуем внедрить в свойство контроллера.
    github.com/gbdrm/aspnet5/commit/3d09ef0c5b6d079884337a6e044262659a4f6250

    Заключение


    Внедрение зависимостей на уровне фреймворка – это отличный механизм для унификации работы с зависимостями. Реализация в ASP.NET 5 прозрачна и понятна, а конфигурация очень легкая. Несмотря на то, что пока реализация находится в бета-версии, большинство функционала уже стабильно и готово к полноценной разработке. Все исходники контейнера доступны на GitHub – github.com/aspnet/DependencyInjection. Работа идет до сих пор, можно ознакомится не только с функциональностью, но и посмотреть, как создают часть платформы. Если вам интересно узнать больше по теме dependency injection в asp.net 5, есть отличная статья (на английском языке): www.emadashi.com/2015/06/dependency-injection-in-asp-net-5-one-step-deeper.

    aspnetcolumngithubСовет! А также, если вам интересна тема ASP.NET5, можно посмотреть примеры, попробовать дописать что-то самому или написать запрос на реализацию какого-либо функционала в проекте с примерами: github.com/gbdrm/aspnet5

    Свежие новости


    Как вы уже знаете выпущена Visual Studio 2015 с ASP.NET5 Beta5. Подробности о том, что именно включено в релиз Visual Studio можно почитать в этом блоге.

    Выпущено обновление ASP.NET5 Beta6 с множеством изменений, улучшений и исправлений ошибок. Подробности обновления можно найти в этом блоге. Вы можете загрузить обновление по этой ссылке.

    Опубликованы планы по выпуску релизов платформы в течение ближайших месяцев до выпуска финальной версии ASP.NET5. Согласно им нас ждут версии Beta7 и Beta8, после чего в ноябре мы получим первую версию, готовую к продакшну (RC1), финальная же версия выйдет в первом квартале 2016 года. Подробности каждой версии можно найти по ссылке.

    Опубликованы доклады конференции DevCon 2015, в том числе по веб-разработке и теме ASP.NET.

    Полезные ссылки


    Самая свежая документация по ASP.NET5 расположена по адресу http://docs.asp.net/en/latest/.

    Приглашаем вас подключаться к живым трансляциям периодического шоу ASP.NET 5 Community Standup, где разработчики из команды Microsoft делятся последними новостями о платформе. Записи доступны по этой ссылке.

    Познакомьтесь с новой статьей о разработке ASP.NET-приложений в Visual Studio Code от Эрика Рейтана, в которой подробно изложены интересные аспекты работы с веб-проектами в VS Code.

    Изучите основы ASP.NET5 с новым бесплатным курсом виртуальной академии Microsoft.

    Авторам


    Друзья, если вам интересно поддержать колонку своим собственным материалом, то прошу написать мне на vyunev@microsoft.com для того чтобы обсудить все детали. Мы разыскиваем авторов, которые могут интересно рассказать про ASP.NET и другие темы.

    Об авторе


    Коцюбан Виктор
    Technical Leader в SoftServe
    Gbdrm

    .NET Разработчик с более чем 8 годами опыта. Специалист в области Enterprise веб проектов. Последние 4 года занимает должность технического лидера.
    Будете ли вы использовать встроенный механизм внедрения зависимостей?

    Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

    Microsoft 260,94
    Microsoft — мировой лидер в области ПО и ИТ-услуг
    Поделиться публикацией
    Комментарии 20
    • 0
      Я так понимаю, использование встроенного DI-контейнера актуально для общих инфраструктурных вещей. Для сервисов бизнес-логики остаются привычные DI-контейнеры с поддержкой регистрации декораторов, коллекций и т.д.
      • 0
        Вообще говоря есть адаптеры к большинству DI-библиотек, реализующие IServiceProvider. Так что подключаете свой любимый контейнер и едете дальше.
        • 0
          Есть небольшая проблема: нет ни одного DI-контейнера, полностью соответствующего поведению контейнера ASP.NET 5. Например, для Unity для регистрации коллекции нужно несколько раз регистрировать сервис. В обсуждении использования Simple Injector с ASP.NET 5 описываются эти проблемы
          • 0
            У того же Ninject-а, вроде, указанной проблемы с коллекциями нет. Коллекции с одним элементом вполне спокойно инжектит. Аналогично у TinyIoC.
      • 0
        одна их первых зависимостей, о которых мы задумаемся для использования DI – это DbContext

        Полюбопытствую: часто ли Вы передаете DbContext в контроллер в реальных приложениях?
        • +2
          Не обязательно же его передавать в контроллер. Но в репозиторий или другой объект с уровня доступа к данным приходится. Просто это хороший пример, который нагляден в демо-приложениях.
        • 0
          Хотелось бы узнать ответ на вопрос в чем преимущество использования этого контейнера перед другими?
          • +2
            Искоробочность.
            • 0
              А при наличии nuget, которым уже фреймворк можно по кускам собирать, это все еще аргумент? Просто есть прекрасные производительные контейнеры которые легко подключаются и удобно настраиваются — какой смысл брать «ис коропки»? Тут как-то возникает подозрение на рецидив любимого not invented here синдрома.
              • 0
                вопрос в том, что если фреймворк внутри себя использует инъекции, нужно ли для этого тащить за собой сторонний инструмент или стоит реализовать легкие контейнеры как часть фреймворка?
                • +2
                  Если б он еще умел все то, что умеют остальные контейнеры… Вот что у него, например, с внедрением Func, если мы зарегистрировали T? Ну и да, а если я вообще не хочу тащить контейнер, а хочу Pure DI? Нафига мне это в инфраструктуре? Учитывая, что в MVC DI контейнер встраивался легко и не принужденно то ли с первой то ли со второй версии, зачем было тратить время на эту разработку не понятно.
                  • 0
                    Ну это как — зачем тащить решарпер если студия тоже умеет рефакторить. Я понимаю любовь некоторых разработчиков к уютненькому «MS-из-коробки», была бы возможность — они бы и из студии не вылазили и пользовались только встроенными тулзами. Но таким способом вы же только стимулируете вендорлок подход и плодите разработчиков которые дальше MSDN в интернете не ходят…
                    • 0
                      Ваша же логика подтвреждает мои слова: никто решарпер не включает в поставку VS, вместо этого предлагают свои инструменты, может быть не такие глубокие, как сторонние.

                      Никто не запретил (по факту наоборот, сделал более широким) применение сторонних контейнеров в ASP.NET.
                      • 0
                        Даже не буду спорить, бесполезно. Для меня это выглядит как размывание аудитории. Вместо 14 конкурирующих контейнеров — у нас теперь будет 15, xkcd #927…

                        У вас же была Unity, везде ее пихали, народ так ужаснулся что написали кучу приличных контейнеров, лишь бы не Unity. С EntityFramework — очень похожая история. Начиная с расцвета microORM и заканчивая тем, что похоже сама команда EF поняла что нахрен этот велосипед, давайте выпилим новый с нуля.

                        Как прекрасно было когда вместо своего DataContractSerializer, MS взяла и задействовала Newtonsoft.Json.NET — в итоге это стандарт де-факто, прекрасная библиотека и легко найти ответы и куча примеров.

                        Ну вот так, без сарказма, вы можете рассказать — а для чего нужен именно свой. Какая практическая долгосрочная цель у этого контейнера — что, МС будет в него вкладываться, создавать и поддерживать сообщество, допиливать фичи для лучшего покрытия edge-cases, исправлять сложные дедлок баги когда они полезут? Бюджет будет на контейнер дальше выделяться? не на asp.net а именно на конкретную вот эту область. Какие планы на дальнейшее развитие этого участка? Или это чтобы hello-world сэмплы было проще писать?
                        • 0
                          Мне кажется, тут нет смысла говорить о политике Microsoft. Если бы это была политика Microsoft, тут был бы Unity или MEF2. Просто команде разрабатывающей MVC захотелось написать свой собственный контейнер с блекджеком. Ну бывает. Бтв, я не заметил, чтобы Unity везде пихали (в смысле настойчиво продвигали). EntityFramework с этой точки зрения более навязчив (по умолчанию добавленный пакет в ASP.Net MVC шаблон проекта).
                          Я то надеялся, что мне расскажут какие новые возможности в контексте MVC приложения дает этот контейнер, по сравнению с другими, но что-то единственное преимущество которое привели — искоробочность, которое я за преимущество не считаю.
                          • 0
                            Когда юнити зарелизили — продвигали довольно настойчиво, потом как-то поубавилось энтузиазизма. А EF это да, как и стандартные шаблоны — так много приходится выкорчевывать, что уже на автомате ищешь только Empty …
                            • 0
                              Когда юнити зарелизили — продвигали довольно настойчиво, потом как-то поубавилось энтузиазизма.

                              Ну это нормально. Надо же чтобы на него обратили внимание и попробовали.
            • +1
              Хорошая статья. Хотелось бы увидеть продолжение не тему использования механизма Options.
              • +3
                Спасибо, одна из причин почему она появилась – ваш комментарий о DI к моей предыдущей статье.
              • +2
                Спасибо за статью.

                Если мы используем EntityFramework в нашем приложении, то одна их первых зависимостей, о которых мы задумаемся для использования DI – это DbContext. Это позволит нам написать менее завязанный на DbContext код, легче тестировать такой функционал и т.д.

                А можете показать пример удобного тестирования при инжекте DbContext?

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое