Русский
Русский
English
Статистика
Реклама

Asp.net

Перевод Тест пропускной способности ASP.NET Core 5.0 в Kestrel, IIS, Nginx и Caddy

05.06.2021 18:10:05 | Автор: admin
Начиная с версии 2.2. ASP.NET Core поддерживает режим внутрипроцессного размещения приложения (InProcess) в IIS, направленный на улучшение производительности кода. Рик Страл написал статью, в которой подробно исследовал эту тему. С тех пор прошло три года, теперь платформа ASP.NET Core добралась до версии 5.0. Как это повлияло на производительность ASP.NET Core-проектов на различных серверах?



Результаты исследования Рика Страла


Рик Страл в своей статье занимался тестирование ASP.NET Core-кода на Windows в Kestrel и в IIS (в режимах InProcess и OutOfProcess). Его интересовало количество запросов в секунду, обрабатываемых системой. В результате он пришёл к выводу о том, что первое место по производительности получает использование IIS в режиме InProcess, второе Kestrel, третье IIS в режиме OutOfProcess.

Обзор эксперимента


Рик не провёл испытания, позволяющие выявить различия в выполнении ASP.NET Core-кода на Windows- и на Linux-серверах. А вопрос о том, что в 2021 году лучше выбрать для проектов, основанных на ASP.NET Core 5.0, интересует многих из тех, кого я знаю. Поэтому я решил, используя подход к тестированию, похожий на тот, которым пользовался Рик, узнать о том, сколько запросов в секунду может обработать ASP.NET Core 5.0-приложение на Windows и на Linux.

Тестовое окружение


Так как Windows 10, Ubuntu Desktop и другие настольные операционные системы не в полной мере отражают особенности сопоставимых с ними серверных дистрибутивов, я решил выбрать для тестов серверные версии соответствующих ОС. Это были свежеустановленные копии систем с последними патчами, перед выполнением тестов они были один раз перезагружены.

Windows-сервер:


  • Провайдер: Microsoft Azure East Asia Region.
  • ОС: Windows Server 2019 Data Center.
  • Характеристики системы: B2S / 2 vCPU, 4GB RAM, Premium SSD.
  • Окружение: IIS с поддержкой статического и динамического сжатия контента, отсутствие интеграции с ASP.NET 3.5 или 4.x, на сервере установлена платформа ASP.NET Core 5.0.2 Runtime.


Сведения о Windows-сервере

Linux-сервер


  • Провайдер: Microsoft Azure East Asia Region.
  • ОС: Ubuntu Server 20.04 LTS.
  • Характеристики системы: B2S / 2 vCPU, 4GB RAM, Premium SSD.
  • Окружение: включено использование BBR, установлены Nginx, Caddy и ASP.NET Core 5.0.2 Runtime.


Сведения о Linux-сервере

Инструменты для проведения тестов


Рик пользовался West Wind Web Surge, но этот инструмент доступен только на платформе Windows, а это нас не устроит. Я решил воспользоваться опенсорсным кросс-платформенным инструментом bombardier, о котором однажды писали в официальном .NET-блоге Microsoft.

Тестовое приложение


Я создал новый проект ASP.NET Core 5.0 Web API, в котором имеется лишь один метод:

[ApiController][Route("[controller]")]public class TestController : ControllerBase{[HttpGet]public string Get(){return $"Test {DateTime.UtcNow}";}}

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

Проект скомпилирован с применением конфигурации Release и опубликован с использованием FDD. Настройки логирования оставлены в стандартном состоянии:

"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}

Методика тестирования


Я запускал проект, используя следующие конфигурации:

  • Kestrel.
  • IIS в режиме InProcess.
  • IIS в режиме OutOfProcess.
  • Обратный прокси Nginx.
  • Обратный прокси Caddy.

Затем я применял bombardier. В течение 10 секунд, по 2 соединениям, велась работа с конечной точкой, доступной на localhost. После прогревочного раунда испытаний я, друг за другом, проводил ещё 3 раунда и на основе полученных данных вычислял показатель количества запросов, обработанных исследуемой системой за секунду (Request per Second, RPS).

Пожалуйста, обратите внимание на то, что в идеальном случае лучше не использовать инструмент для исследования производительности, обращаясь с его помощью к локальным адресам. Дело в том, что при таком подходе операционная система вынуждена будет делить сетевые ресурсы между инструментом для тестирования и исследуемой программой. Но, так как сети в облачных окружениях не так стабильны, как localhost-подключение, я решил использовать в тестах именно локальное подключение. Это позволило исключить воздействие особенностей сети на результаты.

Результаты тестов


Windows + Kestrel


Средний RPS: 18808


Windows + Kestrel

Windows + IIS в режиме InProcess


Средний RPS: 10089


Windows + IIS в режиме InProcess

Windows + IIS в режиме OutOfProcess


Средний RPS: 2820


Windows + IIS в режиме OutOfProcess

Linux + Kestrel


Средний RPS: 10667


Linux + Kestrel

Linux + Nginx


Средний RPS: 3509


Linux + Nginx

Linux + Caddy


Средний RPS: 3485


Linux + Caddy

Итоги


Вот как выглядят результаты тестирования (от самой быстрой комбинации ОС и серверного ПО до самой медленной):

  • Windows + Kestrel (18808)
  • Linux + Kestrel (10667)
  • Windows + IIS в режиме InProcess (10089)
  • Linux + Nginx (3509)
  • Linux + Caddy (3485)
  • Windows + IIS в режиме OutOfProcess (2820)

Мои результаты отличаются от тех, что получил Рик, тестируя ASP.NET Core 2.2-проект. В его тестах производительность IIS в режиме InProcess оказывалась выше, чем производительность Kestrel. Но сейчас Kestrel оказывается быстрее IIS в режиме InProcess, и это кажется вполне логичным и ожидаемым.

А вот неожиданностью для меня стало то, что производительность Windows-серверов c Kestrel оказалась выше производительности аналогичных Linux-серверов. Это меня удивило.

В режиме обратного прокси-сервера Nginx и Caddy, в общем-то, показывают одинаковую производительность. И та и другая конфигурации обходят IIS в режиме OutOfProcess.

Конечно, мой простой тест, в ходе которого сервер возвращает обычную строку, не позволяет проверить все нюансы производительности ASP.NET Core 5.0 и исследовать возможности каждого сервера. В реальных проектах присутствует множество факторов, которые воздействуют на производительность. План моего эксперимента не перекрывает все возможные сценарии использования ASP.NET Core 5.0, в нём, наверное, имеются какие-то недочёты. Если вы что-то такое заметите дайте мне знать.

Дополнение


Мне сообщили, что в .NET 5 у DateTime.UtcNow могут быть проблемы, касающиеся производительности. Я провёл повторные испытания, заменив эту конструкцию на Activity.Current?.Id ?? HttpContext.TraceIdentifier. Получившиеся у меня результаты были выражены немного более высокими показателями, но общая картина осталась почти такой же.

А если увеличить число подключений с 2 до 125 сервер Kestrel, и на Windows, и на Linux, способен дать гораздо более высокую пропускную способность.

Каким серверным ПО вы пользуетесь в своих проектах?


Подробнее..

.NET разработчик, найдись! или история о строителях социальных сетей

04.03.2021 22:20:04 | Автор: admin
Кадр из фильма Офисное пространствоКадр из фильма Офисное пространство

На прошлой неделе технологическая компания, занимающаяся автоматизацией сетей общественного питания, опубликовала в соцсетях фотографию одной из своих точек питания при входе в которую висело объявление о поиске .NET-разработчика. Объявление содержало текст .NET разработчик найдись и QR-код, содержащий ссылку на описание вакансии. Этот нестандартный метод хантинга разработчиков заставил меня вспомнить о событиях 14-летней давности, свидетелем которых мне довелось быть. История, о которой пойдет речь, является примером самого быстрого найма разработчиков из всех, что я видел за свою карьеру. Поскольку я собираюсь рассказать эту историю без прикрас, то я последую примеру Игоря Ашманова и напишу статью в жанре зашифрованных мемуаров. Поэтому вы не увидите здесь ни имен людей, ни названий компаний.

Немного обо мне

К осени 2006 года я уже был опытным веб-разработчиком. За плечами были 3 года опыта коммерческой разработки в крупном ИТ-холдинге и веб-студии. Также был и 1 год опыта некоммерческой разработки, который был получен при создании интернет-портала своего вуза. В тот момент я искал финансовой стабильности, а где ее найти, как не в финансовой сфере, поэтому я устроился работать в Инвестиционную компанию.

Проекты

Я должен был переписать старый интернет-проект о ценных бумагах (далее Первый проект), с Classic ASP на ASP.NET. Поскольку на проекте за один год уже успели смениться 2 разработчика, то сначала нужно было доделать незаконченные задачи и навести небольшой порядок. Я, в принципе, был не против этого, потому что подобные задачи позволяли лучше изучить проект перед его переписыванием.

В первый день работы я узнал, что буду работать отдельно от сотрудников IT-департамента на территории Внутреннего заказчика во Вспомогательном офисе компании. Если быть точным, то это была работа не на территории заказчика, а рядом с территорией заказчика, потому что Внутренний заказчик не горел желанием размещать программиста в своем отделе.

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

Перед самым Новым годом Внутренний заказчик сообщил неожиданную новость, что его отдел стал ответственным за еще один интернет-проект (далее Второй проект) и мне нужно подключиться к работе над ним. Проект был старый и тоже был написан на Classic ASP. Работы по нему должны были стартовать еще в ноябре, но HR-служба не смогла нанять исполнителей.

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

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

Инвестиционная компания

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

Вспомогательный офис

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

Когда я только пришел в компанию, то во Вспомогательном офисе было всего 2 независимых интернет-проекта:

  1. Проект по созданию отечественного аналога eBay (далее я буду называть его Интернет-аукционом), который в основном состоял из .NET-разработчиков.

  2. Экономическая онлайн-игра с очень разнообразной командой технических специалистов: от Flash-разработчика до C++-программиста.

Первоначально мое рабочее место находилось в стеклянной переговорке, в которой располагались сотрудники Интернет-аукциона. Поскольку переговорка была рассчитана лишь на пять человек, а команда росла, то через три недели мне пришлось переехать в другое помещение. Новое помещение уже было полноценной комнатой, в которой располагались сотрудники Онлайн-игры и еще два сотрудника, работающих самостоятельно. С этим коллективом я проработал больше полугода и наиболее яркие воспоминания о компании связаны именно с этим периодом.

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

Из сиюминутных плюшек в компании были только талоны на бизнес-ланч, которыми можно было расплатиться в нескольких ближайших точках общепита. Остальные составляющие соцпакета начинали действовать только через год работы.

Но тем не менее у работы во Вспомогательном офисе были свои плюсы. Там не было суеты характерной для Основного офиса. Поскольку во Вспомогательном офисе работали в основном контентщики и веб-разработчики, то в нем царила тишина, что позволяло сосредоточиться на работе и достигать высокой продуктивности.

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

Строители социальных сетей

В середине февраля из-за изменившейся внешней конъюнктуры нам пришлось запустить Второй проект досрочно. Не успев отойти после его запуска, я услышал от Внутреннего заказчика очередную новость: В рамках этого проекта мы должны запустить Социальную сеть пайщиков. Сделать это нужно до конца апреля.. В принципе, наличие подобного функционала не было для меня неожиданностью, потому что в ранних версиях макетов присутствовал соответствующий раздел, но словосочетание социальная сеть звучало впервые. Несмотря на то, что основная концепция Социальной сети пайщиков была озвучена представителями компании в СМИ еще в январе, оставалось много организационных и юридических деталей, которые Внутренний заказчик должен был учесть при написании ТЗ.

Работы по Второму проекту было и без того много. Постоянный недостаток времени приходилось компенсировать переработками. Мой трудоголизм не раз становился поводом для шуток со стороны коллег (соседей по комнате).

Один раз, придя на работу, я увидел Flash-разработчика, сидящим под дверью комнаты. Я спросил у него: У тебя что нет ключа от комнаты?, на что он мне ответил: А зачем он мне вообще нужен? Если ты постоянно на работе сидишь!.

В другой раз я решил уйти с работы вовремя и когда я уже подошел к двери руководитель Онлайн-игры сказала мне: Хорошо тебе повеселиться! Ты же наверно на какое-то мероприятие идешь? .

Вся эта ситуация делала перспективы переписать Первый проект весьма туманными. Отсутствие возможности писать на .NET заставило меня больше смотреть по сторонам. Во время локального корпоративного мероприятия по случаю 23 февраля в комнате контентщиков, в которой был накрыт праздничный стол, я заметил человека, читающего какое-то руководство по Visual Studio Team System. Мне стало интересно, и я спросил у него: Вы .NET-разработчик?, и он ответил: Почти Я тимлид!. В процессе разговора выяснилось, что, в данный момент, он вместе с Проджект-менеджером из рекламного агентства разрабатывает концепцию Музыкальной социальной сети и в скором времени начнет собирать команду .NET-разработчиков. Я рассказал ему о своем опыте разработки на .NET Framework и SharePoint. На что он мне ответил: .NET Framework 1.1. это конечно хорошо, но на наш проект требуются люди, которые пишут на .NET 2.0 как минимум год причем полный рабочий день без отвлечения на всевозможные легаси-проекты. В общем, стало понятно, что он ищет людей, полностью погруженных в .NET-разработку на протяжении последних нескольких лет. Еще он мне рассказал, что планирует установить TFS и организовать работу команды по методологии MSF.

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

В начале марта на каком-то программистком сайте (sql.ru, relib.com или gotdotnet.ru) я увидел следующий баннер:

В оригинале на этом баннере был указан район, в котором располагались офисы Инвестиционной компании, и адрес электронной почты, содержащий название компании. При щелчке по баннеру осуществлялся переход на описание вакансии:

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

Надо признать, что Проджект-менеджер хорошо разбирался в таргетинге интернет-рекламы, потому что эта информация практически мгновенно дошла до своей целевой аудитории. На обеденном перерыве я зашел к разработчикам Интернет-аукциона, и, как выяснилось, они тоже видели этот баннер и были впечатлены размером заработной платы. Через несколько дней со мной по ICQ связался коллега с предыдущего места работы и начал узнавать у меня, что я знаю об этой вакансии. Я объяснил ему, что там требуется серьезный опыт работы с .NET 2.0. На что он ответил мне: Пусть у меня нет особого опыта работы с этой версией, но я все равно отправлю туда резюме, а потом, если что подтяну свои навыки. Поскольку он больше мне не писал, то думаю, что он не смог пройти первоначальный отбор.

Ближе к концу марта появилось некое подобие ТЗ на Социальную сеть пайщиков. Запуск планировалось производить в несколько этапов. Первый этап предполагал интеграцию с одной из внутренних учетных систем. Мы встретились с сотрудниками Отдела автоматизации, отвечающим за эту систему, и договорились, что они напишут для нас хранимые процедуры, которые будут возвращать нужные нам данные.

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

По моим подсчетам у Тимлида ушло не больше двух недель на подбор трех .NET-разработчиков, потому что уже в 20-х числах марта нанятые им сотрудники вышли на работу.

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

В тоже время опытных .NET-разработчиков было немного. Несмотря на то, что .NET Framework существовал уже пять лет, это все еще был переходный период. Мало кто из опытных разработчиков, начинал свой путь с .NET, потому что до версии 1.1 это была малопригодная для коммерческой разработки технология. На .NET в основном переходили разработчики, писавшие на следующих технологиях: Visual Basic, Classic ASP, Delphi и Visual J++. Осуществить такой переход было гораздо сложение чем сейчас перейти с .NET Framework на .NET Core или .NET 5. Как правило, у новоявленных .NET-разработчиков все еще оставалось на поддержке какое-то количество легаси-кода.

Помимо нестандартного размещения вакансии, у столь быстрого завершения поиска сотрудников были и другие объяснения:

  1. Деньги решают все. Нижняя планка зарплаты была, конечно, хорошая, а вот верхняя, в те годы, могла заставить сильных разработчиков покинуть свои насиженные места. Многих не остановили бы даже не совсем комфортные условия работы. Но, с точки зрения работодателя, здесь был один минус. Например, разработчики Интернет-аукциона, имевшие сеньорские должности, ознакомившись с текстом вакансии в тот же день пошли в HR-службу компании требовать прибавку к зарплате.

  2. Отсутствие бюрократических барьеров. Поскольку найм производился непосредственно Тимлидом, а офис находился в квартире жилого дома, то отсутствовали многие бюрократические барьеры: заказ гостевых пропусков у администрации бизнес-центра, бронирование переговорок, первый этап собеседования с HR-специалистом и согласование каждой кандидатуры с вышестоящим руководством.

  3. Понятные критерии подбора. Потенциальных сотрудников оценивали исключительно по хард-скиллам. Причем большинство этих хард-скиллов было стандартизировано, потому что при разработке планировалось использовать исключительно технологии, инструменты и методологии одного вендора Microsoft. Сейчас такое сложно представить, но тогда это было нормой. Кроме того, это был совершенно некорпоративный найм. Если сейчас некоторые компании заявляют об отсутствии у них корпоративности, то, скорее всего, это является частью их корпоративной культуры. Никто не проверял кандидатов на соответствие корпоративной культуре, потому что сами нанимающие не были с ней знакомы.

На этом можно было бы закончить статью и добавить все описанное выше в качестве примера в учебники по IT-рекрутменту. Но тогда статья не была бы честной. Поэтому я расскажу историю до конца.

Меняющаяся реальность

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

И как я уже говорил раньше, многое в компании зависело от внешней конъюнктуры. В конце марта один Интернет-гигант купил Популярную в Рунете социальную сеть. Технически она была очень грамотно реализована. Тогда уже многие сайты использовали в своих интерфейсах AJAX, но не все использовали Unobtrusive (ненавязчивый) JavaScript. Данный подход позволял сайтам работать одинаково хорошо, как с включенным в браузере JavaScript, так и с отключенным.

Определенная часть команды Популярной социальной сети не стала переходить на работу в Интернет-гигант. Инвестиционная компания имела обширные связи в интернет-тусовке и поэтому практически мгновенно смогла выйти на этих людей и предложить им работу.

В начале апреля Проджект-менеджер объявил команде о закрытии проекта Музыкальной социальной сети.

Аудиторы интернет-проектов

Сразу же из команды Музыкальной социальной сети уволился один разработчик. После этого к решению проблемы подключилась HR-служба компании. Эйчары не могли допустить, чтобы только что нанятых высокооплачиваемых сотрудников просто выставили на улицу. Поэтому они начали искать варианты трудоустройства Тимлида и двух разработчиков внутри компании. В тот момент, в IT-департаменте просто не было физического места для размещения новых сотрудников. Единственным местом трудоустройства оставались независимые интернет-проекты Вспомогательного офиса. Было принято решение отправить по одному разработчику в каждый из проектов. Их задачей было провести технический аудит проектов и устранить найденные недоработки. Отчитываться они должны были Тимлиду, который, в свою очередь, должен был отчитываться перед руководством Инвестиционной компании.

Тимлид переехал обратно в комнату контентщиков. В комнату, в которой сидели разработчики Онлайн-игры и я, переехал самый опытный .NET-разработчик. Поскольку помимо технического образования у него также было и духовное образование, то мы между собой называли его Семинаристом. К разработчикам Интернет-аукциона переехал второй .NET-разработчик, которого там прозвали Велосипедистом за то, что он ездил на работу на велосипеде.

Семинарист обладал обширными знаниями по последним технологиям Microsoft. Из любопытства я спросил у него о том, как он смог так сильно прокачаться. На что он мне ответил: Последние полгода я работал Полугосударственной компании, в которой были продолжительные простои. Мы очень долго ждали пока бизнес-аналитики сформулируют свои требования. Кто-то во время этих простоев играл в Counter-Strike, а я и еще несколько человек прокачивались. Я оттуда уволился, чтобы больше разработкой заниматься, а сейчас вообще неизвестно чем занимаюсь.

В 20-х числах апреля Отдел автоматизации наконец-то предоставил мне хранимые процедуры. Нерешенной оставалась проблема добавления в учетную систему тестовых записей, на основе которых мы могли бы завести тестовые аккаунты. Ни айтишники, ни бизнес не могли решить эту проблему. Кто-то говорил, что так нельзя из соображений безопасности, а кто-то, что это невозможно юридически. В общем, мы шли по кругу. В какой-то момент Внутренний заказчик сказал мне, что договорился с руководителем Подразделения, занимающегося паями. И я услышал от него следующее: В общем, возьми паспорт и ИНН, и сходи в Основной офис и купи себе пай. Я так уже и поступил. На следующий день я пришел в Основной офис и обратился к сотруднику фронт-офиса, контакты которого мне дал Внутренний заказчик. Заполнив все анкеты, я внес через кассу свои собственные деньги. После того, как мне выдали все необходимые документы, сотрудник фронт-офиса сказал мне: Ой! Вы извините, но мы Вам случайно комиссию за оформление посчитали. Но я думаю, что ничего страшного из-за этого не случится.. Это было самое запоминающиеся оформление тестового аккаунта в моей жизни ;-)

Через пару дней Внутренний заказчик, занятый организационными вопросами запуска Социальной сети пайщиков, перестал отвечать на звонки и почту. После некоторого времени в мой почтовый ящик посыпались письма людей, которые не могли решить свои вопросы с Внутренним заказчиком. Однозначно истолковать суть этих писем было сложно, т.к. в них приводились ссылки на какие-то ранние договоренности с Внутренним заказчиком. Я обратился к нему за разъяснениями, на что он ответил: Игнорируй их и занимайся своей работой.. Сотрудники отдела Внутреннего заказчика, в принципе, были обычными контентщиками, поэтому часть звонков они также стали переводить на меня. Поскольку в моей комнате был всего один телефон, то это очень мешало работе разработчиков Онлайн-игры. И в один в прекрасный день мне позвонила Новый руководитель одного из маркетинговых подразделений. Первое, что я услышал: Почему Вас нельзя найти ни в IT-департаменте, ни в отделе Внутреннего заказчика? Когда вы заверстаете присланные мной макеты?. На это я ей ответил, что эти макеты содержат HTML-формы и вывод данных в виде таблиц, и одних графических файлов мало, т.к. нужен какой-то пояснительный текст. Ее ответ был следующим: Внутренний заказчик сказал, что работа по этой задаче ведется уже давно!. Поскольку до этого я не видел эти макеты и не имел никакой другой информации, то сказал ей, что, скорее всего, Внутренний заказчик пишет по этой задаче ТЗ. На что получил ответ: Вы не хотите работать!!!, затем разговор перешел на повышенные тона с обоих сторон. Весь этот бесконечный поток информации говорил только об одном, что задачи, стоящие в наших планах, были лишь верхушкой айсберга.

Несмотря на все отвлекающие факторы, между майскими праздниками мы все-таки запустили нашу социальную сеть.

В начале мая Вспомогательный офис начал расширяться. В качестве дополнительного помещения стала использоваться квартира на последнем этаже, из которой в конце апреля съехали какие-то англичане. Одними из первых туда переехали разработчики Интернет-аукциона. В их стеклянную переговорку должно было въехать новое подразделение, состоящее из очень известных деятелей Рунета. Новое помещение имело совершенно другую планировку. Разработчики Интернет-аукциона очень долго смеялись, когда в санузле обнаружили джакузи. Другой интересной особенностью этой квартиры было то, что внутри помещения рядом с дверью был стол, за которым сидел мужчина в строгом деловом костюме. Неосведомленные люди принимали его за охранника, но на самом деле он был финансистом, занимавшим директорскую должность, и по совместительству преподавателем учебного центра (его, кстати, сейчас часто показывают по телевизору).

Тем временем аудиторы интернет-проектов продолжали делать свою работу. Их основной задачей было, что-то найти. И они, как правило, что-то находили. В Онлайн-игре ничего особо серьезного не нашли, а вот в коде Интернет-аукциона был найден какой-то участок кода, имевший проблемы с производительностью. Тимлид на основе информации, полученной от Велосипедиста, записал нагрузочный тест и запустил его на stage-сервере. Сервер упал, причем упал в какой-то неподходящий момент, тем самым вызвав серьезное раздражение у разработчиков Интернет-аукциона. Если бы, тогда они сидели на одном этаже, то дело могло дойти и до драки. Аудиторы поняли, что дальше так работать уже нельзя и Тимлид встретился с руководством IT-департамента. В IT-департаменте существовал проект по разработке Социальной сети трейдеров. Внутри компании за него отвечали два .NET-разработчика, также над ним работали дизайнер и верстальщик, но они находились за пределами компании. Проект буксовал из-за разногласий .NET-разработчиков с дизайнером. В основном, эти проблемы были связаны со специфической HTML-разметкой, создаваемой технологией ASP.NET Web Forms. В общем, разработчики ни в чем не были виноваты. Руководство предложило усилить эту команду Тимлидом, Семинаристом и Велосипедистом. Проблема с отсутствием свободного места должна была решиться в конце мая или начале июня, после окончания ремонта новых помещений для IT-департамента.

Тимлид рассказал об этом варианте своей команде. После чего они даже сходили в Основной офис и пообщались с командой проекта. Семинариста этот вариант не устроил, потому что он больше не хотел связываться с легаси-кодом. Поэтому он отдельно от всех встретился с Проджект-менеджером и руководством Рекламного агентства. Он убедил их, что сам способен собрать команду разработчиков и запустить любой проект. Рекламное агентство заинтересовалось его предложением и выдвинуло идею проекта с очень оригинальной концепцией.

Во вспомогательный офис прибывали все новые и новые люди. В один прекрасный день ко мне подошел Руководитель контентщиков, который по факту отвечал за весь Вспомогательный офис, и сказал следующее: Тебе скоро придется покинуть это помещение. Куда тебя переселять не знаю. Не исключен и вариант переезда в коридор. В общем, решай этот вопрос с Внутренним заказчиком. В принципе, я знал, что этот день когда-нибудь наступит, но также я знал от Семинариста о новых помещениях IT-департамента. Поскольку это была уже не первая конфликтная ситуация за этот месяц, то я решил, что терять мне особо нечего и начал добиваться перевода в Основной офис. После долгой и не очень приятной переписки с Внутренним заказчиком, IT-директором и HR-директором, мне одобрили перевод в Основной офис, а также много чего наобещали.

Эпилог

Наступил конец мая. Вечером Тимлид, Велосипедист и я, держа в руках свои вещи, шли по улице в направлении Основного офиса. Впереди нас шли два грузчика и катили тележки с нашим оборудованием. На последнем этаже здания Основного офиса в большом зале, разделенном перегородками, нас ждали новые рабочие места. Поскольку в некоторых частях помещения еще шел ремонт, то мы были одними из первых, кто туда переехал. На столах были приклеены бумажки с нашими фамилиями, сняв их мы поняли, что опять являемся соседями.

Тимлид возглавил группу разработки Социальной сети трейдеров. Данная социальная сеть существует и по сей день. Работая в IT-департаменте ему часто приходилось нанимать разработчиков. Делал он это по-прежнему эффективно, но не так быстро, как во Вспомогательном офисе. Однажды, чтобы срочно закрыть вакансию ему пришлось переманить разработчика из команды Семинариста. В итоге он дорос до должности начальника отдела интернет-проектов. После 5 лет работы в Инвестиционной компании он переехал на западное побережье США.

Велосипедист принял активное участие в запуске новой версии Социальной сети трейдеров, но через полгода покинул компанию. Через некоторое время он устроился ведущим разработчиком в Крупнейший отечественный интернет-магазин, где проработал почти семь лет. Где он работает сейчас неизвестно.

Семинарист возглавил группу по разработке достаточно оригинального проекта, который можно охарактеризовать так: социальная сеть + тамагочи. После 4 лет работы в Инвестиционной компании перешел в Крупную нефтегазовую компанию, отработав там около года переехал на юг Евросоюза.

Я же через месяц после переезда закончил работу над оставшимся функционалом Социальной сети пайщиков. Затем запустил ряд сервисов в рамках второго проекта. Около полугода потратил на поиск разработчика на Classic ASP. В процессе поиска принял Третий легаси-проект, получив таким образом статус Главного и единственного подрядчика Внутреннего заказчика. Два раза хотел уволиться, но меня не отпускали. В итоге нанял .NET-разработчика для последующего переобучения на Classic ASP (своего рода профессиональный даунгрейд). Закончив все крупные задачи понял, что наконец-то могу уволиться, но просчитался с моментом. Через месяц после моего ухода начался кризис 2008 года. По прошествии двух месяцев устроился в более крупную компанию, в которой у меня появились проекты на .NET.

Внутренний заказчик на пике популярности Социальной сети пайщиков успел дать несколько интервью авторитетным финансовым изданиям, в которых подробно рассказал о принципах работы проекта. Примерно через полгода после моего ухода он покинул свой отдел, чтобы возглавить Социальную сеть трейдеров, технической частью которой занимался Тимлид. Из своих старых проектов Внутренний заказчик оставил за собой только Социальную сеть пайщиков. Социальная сеть пайщиков просуществовала около 4 лет и была закрыта одновременно с уходом Внутреннего заказчика из компании.

Инвестиционная компания успешно пережила кризис 2008 года и через некоторое время купила здание. Сейчас в этом здание работает большая часть сотрудников компании.

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

Подробнее..

Улучшения нового редактора Razor в Visual Studio

27.01.2021 16:20:11 | Автор: admin

Прошло шесть месяцев с тех пор, как мы анонсировали первую предварительную версию нового экспериментального редактора Razor для Visual Studio, основанного на общем языковом сервере Razor, и пришло время рассказать о наших успехах. Команда усердно работала над приведением нового редактора Razor в соответствие со старым, исправлением ошибок и добавлением множества замечательных новых функций. Мы думаем, что новый редактор близок к тому, чтобы быть готовым к нормальной повседневной разработке, поэтому сейчас самое время попробовать его с последней предварительной версией Visual Studio. Мы надеемся, что вы попробуете новый редактор Razor, и поделитесь с нами своими впечатлениями!

Начало работы

Чтобы начать работу с новым редактором Razor :

  1. Установите последнююVisual Studio preview (16.9 Preview 3).

    • Заметка: Превью-версии Visual Studio можно безопасно устанавоивать вместе со стабильной установкой Visual Studio .

  2. ПерейдитеTools>Options>Environment>Preview Featuresи выберите опциюEnable experimental Razor editor:

Когда вы собираетесь включить новый редактор Razor, вы можете обнаружить, что он уже включен. Начиная с Visual Studio 16.9 Preview 3, мы постепенно развертываем новый редактор Razor для различных групп пользователей. Если вы опробовали новый редактор ранее, а затем отключили его, вы не будете участвовать в автоматическом внедрении. Вам нужно будет повторно включить его вручную, чтобы увидеть весь прогресс, которого мы достигли. Многие из известных проблем с новым редактором теперь исправлены, поэтому стоит попробовать еще раз, если вы столкнулись с проблемами в более ранних сборках.

Что нового?

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

Улучшенное форматирование

В новом редакторе Razor есть улучшенный механизм форматирования, который более консервативен, чем старый (во-первых, не навреди!), а также намного умнее в области обработки кода.

Мы стремимся исправить форматирование Razor, чтобы оно заставляло вас улыбаться с новым редактором, поэтому сообщите нам, если у вас возникнут проблемы с форматированием, и мы обязательно их исправим.

C# code actions

Некоторые действия кода C# теперь доступны в файлах Razor:

  • Добавьте директивы @using или полные имена типов.

  • Добавьте null checks.

Дизайн нового редактора Razor значительно упрощает включение действий кода C#, и мы ожидаем, что в будущих выпусках будет доступно гораздо больше.

Переименовывание в закрытых файлах

Переименование больше не ограничивается открытыми файлами Razor. Имена в закрытых файлах Razor также будут обновлены.

Переименовывание компонентов Blazor

Теперь вы можете переименовать компонент Blazor из его тега. Компонентный файл Razor будет переименован автоматически.

Действия кода компонента

Также теперь доступны несколько действий кода для конкретных компонентов:

  • Создать компонент из неизвестного тега.

  • Извлечь @code в код программной части.

  • Добавить @using для компонентов или полностью квалифицированный тег компонента.

Перейти к определению компонента

Вам нужно быстро увидеть код этого компонента? Просто нажмите F12 на теге, и вы на месте!

Изменение Razor с LiveShare

Новый редактор Razor также работает с LiveShare, поэтому вы получите все новые возможности редактирования Razor даже при работе с Razor в удаленном сеансе.

Используйте новый редактор Razor с Visual Studio Code

Поскольку новый редактор Razor основан на переиспользуемом языковом сервере Razor, новый редактор Razor и его новые функции также доступны в Visual Studio Code с установленным расширением C#.

А как насчет Visual Studio для Mac? Visual Studio для Mac пока не поддерживает LSP, но как только поддержка появится, мы добавим новый редактор Razor в Visual Studio для Mac.

Улучшения подсветки синтаксиса Razor

Мы также работаем над некоторыми улучшениями в подсветке синтаксиса Razor в новом редакторе, о которых мы хотели бы узнать ваши отзывы. Пожалуйста, поделитесь с нами своим мнением, приняв участие в следующих опросах, если вы еще этого не сделали:

Ждем вашу обратную связь

Если у вас возникла проблема с новым редактором, лучший способ сообщить нам об этом - использовать функциюSend Feedback > Report a Problemв Visual Studio.

Подробнее..

Перевод Что из себя представляет класс Startup и Program.cs в ASP.NET Core

15.02.2021 16:13:26 | Автор: admin

В преддверии старта курса C# ASP.NET Core разработчик подготовили традиционный перевод полезного материала.

Также приглашаем на открытый вебинар по теме
Отличия структурных шаблонов проектирования на примерах. На вебинаре участники вместе с экспертом рассмотрят три структурных шаблона проектирования: Заместитель, Адаптер и Декоратор; а также напишут несколько простых программ и проведут их рефакторинг.


Введение

Program.cs это место, с которого начинается приложение. Файл Program.cs в ASP.NET Core работает так же, как файл Program.cs в традиционном консольном приложении .NET Framework. Файл Program.cs является точкой входа в приложение и отвечает за регистрацию и заполнение Startup.cs, IISIntegration и создания хоста с помощью инстанса IWebHostBuilder, метода Main.

Global.asax больше не входит в состав приложения ASP.NET Core. В ASP.NET Core заменой файла Global.asax является файл Startup.cs.

Файл Startup.cs это также точка входа, и он будет вызываться после выполнения файла Program.cs на уровне приложения. Он обрабатывает конвейер запросов. Класс Startup запускается в момент запуска приложения.

Описание

Что такое Program.cs?

Program.cs это место, с которого начинается приложение. Файл класса Program.cs является точкой входа в наше приложение и создает инстанс IWebHost, на котором размещается веб-приложение.

public class Program {      public static void Main(string[] args) {          BuildWebHost(args).Run();      }      public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args).UseStartup < startup > ().Build();  }  

WebHost используется для создания инстансов IWebHost, IWebHostBuilder и IWebHostBuilder, которые имеют предварительно настроенные параметры. Метод CreateDefaultBuilder() создает новый инстанс WebHostBuilder.

Метод UseStartup() определяет класс Startup, который будет использоваться веб-хостом. Мы также можем указать наш собственный пользовательский класс вместо Startup.

Метод Build() возвращает экземпляр IWebHost, а Run() запускает веб-приложение до его полной остановки.

Program.cs в ASP.NET Core упрощает настройку веб-хоста.

public static IWebHostBuilder CreateDefaultBuilder(string[] args) {      var builder = new WebHostBuilder().UseKestrel().UseContentRoot(Directory.GetCurrentDirectory()).ConfigureAppConfiguration((hostingContext, config) => {          /* setup config */ }).ConfigureLogging((hostingContext, logging) => {          /* setup logging */ }).UseIISIntegration()      return builder;  }

Метод UseKestrel() является расширением, которое определяет Kestrel как внутренний веб-сервер. Kestrel это кроссплатформенный веб-сервер для ASP.NET Core с открытым исходным кодом. Приложение работает с модулем Asp.Net Core, и необходимо включить интеграцию IIS (UseIISIntegration()), которая настраивает базовый адрес и порт приложения.

Он также настраивает UseIISIntegration(), UseContentRoot(), UseEnvironment(Development), UseStartup() и другие доступные конфигурации, например Appsetting.json и Environment Variable. UseContentRoot используется для обозначения текущего пути к каталогу.

Мы также можем зарегистрировать логирование и установить минимальный loglevel, как показано ниже. Это также переопределитloglevel, настроенный в файле appsetting.json.

.ConfigureLogging(logging => { logging.SetMinimumLevel(LogLevel.Warning); }) 

Таким же образом мы можем контролировать размер тела нашего запроса и ответа, включив эту опцию файле program.cs, как показано ниже.

.ConfigureKestrel((context, options) => { options.Limits.MaxRequestBodySize = 20000000; });  

ASP.net Core является кроссплатформенным и имеет открытый исходный код, а также его можно размещать на любом сервере (а не только на IIS, внешнем веб-сервере), таком как IIS, Apache, Nginx и т. д.

Что такое файл Startup?

Обязателен ли файл startup.cs или нет? Да, startup.cs является обязательным, его можно специализировать любым модификатором доступа, например, public, private, internal. В одном приложении допускается использование нескольких классов Startup. ASP.NET Core выберет соответствующий класс в зависимости от среды.

Если существует класс Startup{EnvironmentName}, этот класс будет вызываться для этого EnvironmentName или будет выполнен файл Startup для конкретной среды в целом (Environment Specific), чтобы избежать частых изменений кода/настроек/конфигурации в зависимости от среды.

ASP.NET Core поддерживает несколько переменных среды, таких как Development, Production и Staging. Он считывает переменную среды ASPNETCORE_ENVIRONMENT при запуске приложения и сохраняет значение в интерфейсе среды хоста (into Hosting Environment interface).

Обязательно ли этот класс должен называться startup.cs? Нет, имя класса не обязательно должно быть Startup.

Мы можем определить два метода в Startup файле, например ConfigureServices и Configure, вместе с конструктором.

Пример Startup файла

public class Startup {      // Use this method to add services to the container.      public void ConfigureServices(IServiceCollection services) {          ...      }      // Use this method to configure the HTTP request pipeline.      public void Configure(IApplicationBuilder app) {          ...      }  }  

Метод ConfigureServices

Это опциональный метод в классе Startup, который используется для настройки сервисов для приложения. Когда в приложение поступает какой-либо запрос, сначала вызывается метод ConfigureService.

Метод ConfigureServices включает параметр IServiceCollection для регистрации сервисов. Этот метод должен быть объявлен с модификатором доступа public, чтобы среда могла читать контент из метаданных.

public void ConfigureServices(IServiceCollection services)  {     services.AddMvc();  }  

Метод Configure

Метод Configure используется для указания того, как приложение будет отвечать на каждый HTTP-запрос. Этот метод в основном используется для регистрации промежуточного программного обеспечения (middleware) в HTTP-конвейере. Этот метод принимает параметр IApplicationBuilder вместе с некоторыми другими сервисами, такими как IHostingEnvironment и ILoggerFactory. Как только мы добавим какой-либо сервис в метод ConfigureService, он будет доступен для использования в методе Configure.

public void Configure(IApplicationBuilder app)  {     app.UseMvc();  }

В приведенном выше примере показано, как включить функцию MVC в нашем фреймворке. Нам нужно зарегистрировать UseMvc() в Configure и сервис AddMvc() в ConfigureServices. UseMvc является промежуточным программным обеспечением. Промежуточное программное обеспечение (Middleware) это новая концепция, представленная в Asp.net Core. Для вас доступно множество встроенных промежуточных программ, некоторые из которых указаны ниже.

app.UseHttpsRedirection();  app.UseStaticFiles();  app.UseRouting();  app.UseAuthorization();  UseCookiePolicy();  UseSession(); 

Промежуточное программное обеспечение можно настроить в http-конвейере с помощью команд Use, Run и Map.

Run

Суть Run заключается в немедленном замыкании HTTP-конвейера. Это лаконичный способ добавления промежуточного программного обеспечения в конвейер, который не вызывает никакого другого промежуточного программного обеспечения, которое находится рядом с ним, и немедленно возвращает HTTP-ответ.

Use

Это передаст следующему (параметр next) делегату, так что HTTP-запрос будет передан следующему промежуточному программному обеспечению после выполнения текущего, если следующий делегат есть.

Map

Map просто принимает путь и функцию, которая конфигурирует отдельный конвейер промежуточного программного обеспечения.

app.Map("/MyDelegate", MyDelegate);

Чтобы получить более подробную информацию о промежуточном программном обеспечении, переходите сюда

ASP.net Core имеет встроенную поддержку внедрения зависимостей (Dependency Injection). Мы можем настроить сервисы для контейнера внедрения зависимостей, используя этот метод. Следующие способы конфигурационные методы в Startup классе.

AddTransient

Transient (временные) объекты всегда разные; каждому контроллеру и сервису предоставляется новый инстанс.

Scoped

Scoped используются одни и те же объекты в пределах одного запроса, но разные в разных запросах.

Singleton

Singleton объекты одни и те же для каждого объекта и каждого запроса.

Можем ли мы удалить startup.cs и объединить все в один класс с Program.cs?

Ответ: Да, мы можем объединить все классы запуска в один файл.

Заключение

Вы получили базовое понимание того, почему файлы program.cs и startup.cs важны для нашего приложения Asp.net Core и как их можно настроить. Мы также немного познакомились с переменной среды (Environment Variable), внедрение зависимостей, промежуточным программным обеспечением и как его настроить. Кроме того, мы увидели, как можно настроить UseIIsintegration() и UseKestrel().


Узнать подробнее о курсе C# ASP.NET Core разработчик.

Смотреть открытый вебинар по теме Отличия структурных шаблонов проектирования на примерах.

Подробнее..

Перевод Блеск и нищета open source платформы RawCMS. Причины провала и выводы

23.04.2021 18:05:42 | Автор: admin

Я люблю открытое ПО. Я начал разрабатывать сторонний проект с открытым исходным кодом в 2006 году, и это был секрет развития моей карьеры. Благодаря моим экспериментам того времени, надеюсь, я вырос как разработчик и возвращаю что-то сообществу Open Source. По-моему мнению, открытый исходный код это драйвер роста компаний и разработчиков. И сегодня я хочу рассказать о своём опыте начавшейся в 2018 году работы над платформой low-code с открытым исходным кодом под названием RawCMS.


Провал

RawCMS начиналась как сторонний проект по улучшению будущего Asp.net core 3.1 и изучению возможности работы с неструктурированными данными для ускорения процесса разработки.

Во время разработки я столкнулся со многими проблемами и, наконец, пришёл к работающему доказательству концепции. Результат был колоссальным, и я должен много раз поблагодарить всех его участников (а их более 20).

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

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

Считал и до сих пор считаю, что в определении продукта не было пробелов. RawCms стремилась сделать то, что действительно понадобится разработчику в будущем, но RawCMS было слишком сложно понять.

Разрешение всей сложности, с которой мы столкнулись на своём пути, было великолепным, но, чтобы бороться со сложностью, нам нужна была сложность. Это мешало первоначально разобраться в логике приложения и внести в него свой вклад.

Вся эта сложность пугала разработчиков на подходе.

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

На этом этапе, прежде чем забыть о проекте, я хотел подумать о том, что сработало, а что нет в ретроспективе.

Окончательный вывод: для наших нужд мы выбрали худшую технологию.

Чтобы убедить человека внутри меня, который отказался от правды, я переосмыслил проект с самого начала. Используя Symfony вместо .Net, я потратил на работу чуть больше двух дней и получил работающее доказательство концепции. Я реализовал не весь набор функций, но почти уверен, что в новых обстоятельствах мы не обнаружим большое количество проблем. Это многому меня научило.

Как я смог за два дня сделать то же, что за два года?

Ответ на этот вопрос сильно меня напугал, было интересно, где я ошибался. В следующих разделах я расскажу, в чём были мои ошибки и как можно было сделать лучше.

Подходящий инструмент

Я не опоздал с .Net. Работаю с платформой с 2005 года, реализовал множество крупных корпоративных проектов, и меня приглашали на множество конференций, чтобы я поделился своим опытом.

Но я просто подобрал не тот инструмент для стороннего проекта. Я по-прежнему считаю, что .Net вероятно, лучший фреймворк для корпоративных приложений, а язык C# не имеет себе равных, с точки зрения производительности и впечатлений от разработки.

Проблема заключалась вот в чём: я выбрал .NET только потому, что это был фреймворк, в котором я чувствую себя более уверенно. Для меня выбор .Net был возможностью успеха. И я ошибался.

С тех пор как я начал этот проект, мне пришлось справляться с некоторыми функциями, которые .Net не поддерживает нативно; каждая отдельная проблема была преодолением.Я говорю об управлении нетипизированными данными (в частности маппинг и GraphQL), имея плагинную архитектуру и встраивая в неё модульный SPA.

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

Упорствовать вошибке от лукавого

Ошибки от человека, но упорство в ошибке от лукавого. Пытаясь заставить всё работать, я понял это на собственном опыте. Для меня это был важный урок.Сдаться перед огромной проблемой не всегда проявление слабости, а иногда, может быть, доказательство того, что вы умны. Я не остановился перед трудностями и победил зверя, но что потом?

Цена проделанной работы, как и её трудность, была огромной, я имею в виду, что много времени и усилий потрачено впустую, за исключением замечательного опыта и компетенций, которые позволили мне (виртуально) путешествовать по миру и рассказывать свою историю.

Что я понял? Вместе нам никогда не приходилось сдаваться перед проблемой, но столкнувшись со сложностью один на один...

Спросите себя, в чём проблема в самой проблеме или в вас?

Ещё до кодинга расскажите, как видите продукт

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

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

Они, вероятно, также не могли понять, почему их попросили внести какие-то изменения. Конечный результат я потратил много времени, чтобы ознакомить с проектом начинающих контрибьюторов, выполнив только несколько небольших задач.

Этого бы не произошло, если бы система была проще и была основана на популярном фреймворке, таком как Symphony, с сообществом, где разработчики смогли бы найти ответы на свои вопросы.

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

Выводы

Мне по-прежнему нравится вносить свой вклад в мир открытого исходного кода. Думая о потраченном на RawCMS времени, на самом деле я не чувствую, что оно вообще потеряно.

Если подумать о реальных результатах, то да, это был провал. Я потратил много времени, и у меня остался только наполовину работающий проект без пользователей. Я не заработал денег, никто не благодарил меня за то, что я сделал. Ситуация кажется игрой, в которой невозможно выиграть, но не думаю, что это так. Я всё ещё убеждён в ценности открытого исходного кода.

И вот какую пользу я извлёк из этого опыта:

  • Многое узнал о современных технологиях, что умножает мою ценность как профессионала.

  • Был приглашён на множество международных конференций, чтобы прояснить то, чему научился.

  • Помог начинающим разработчикам сделать первый шаг.

  • Встретил много замечательных людей, которые, как и я, верят в дух открытого исходного кода.

  • Вернул что-то сообществу открытого ПО.

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

Но ценный опыт можно получить и проще, например прокачав себя в разработке на направлении C#-разработчик. На нём можно не только повысить свою квалификацию, но и пообщаться с опытными менторами, которые разъяснят непонятные моменты.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Бесплатная электронная книга на русском Blazor для разработчиков ASP.NET Web Forms

18.08.2020 10:04:18 | Автор: admin
Привет, Хабр! Как многие знают, мы очень активно развиваем платформу Blazor, клиентскую платформу веб-интерфейса, основанную на C#, а не на JavaScript. С помощью Blazor вы можете писать собственную клиентскую логику и компоненты пользовательского интерфейса на C#, компилировать их в обычные сборки .NET, а затем запускать их непосредственно в браузере с помощью нового открытого веб-стандарта WebAssembly.

Для лучшего понимания принципов работы с платформой мы создали бесплатную книгу "Blazor для разработчиков ASP.NET Web Forms". Сейчас она еще находится в процессе доработки, но уже доступна на многих языках. Оригинал на английском можно скачать как PDF, а перевод на русский доступен на нашем сайте с документацией.

Под катом вы найдете все полезные ссылки, а также сможете прочитать введение. Присоединяйтесь!



Ссылки на книгу



Вступление


.NET уже давно поддерживает разработку веб-приложений через ASP.NET комплексный набор платформ и средств для создания любых типов приложений.ASP.NET имеет собственную линейку веб-платформ и технологий, начиная с классического ASP.Такие платформы, как ASP.NET Web Forms, ASP.NET MVC, веб-страницы ASP.NET и более поздняя ASP.NET Core, предоставляют эффективный и функциональный способ создания веб-приложений,подготавливаемых к просмотру на сервере, в которых содержимое пользовательского интерфейса динамически формируется на сервере в ответ на HTTP-запросы.Каждая платформа ASP.NET ориентирована на определенную аудиторию и подход к созданию приложений.

ASP.NET Web Forms поставляется с исходным выпуском .NET Framework и поддержкой веб-разработки с использованием множества шаблонов, знакомых разработчикам классических приложений, таких как многократно используемые элементы управления пользовательского интерфейса с простой обработкой событий.Однако ни одно из предложений ASP.NET не предоставляет способа для выполнения кода, выполняемого в браузере пользователя.Для этого требуется написать код JavaScript и использовать любые из множества платформ и средств JavaScript, которые набирали и теряли популярность в течение многих лет: jQuery, Knockout, Angular, React и т.д.

Blazor это новая веб-платформа, которая расширяет возможности создания веб-приложений с помощью.NET.Blazorпредставляет собой клиентскую платформу веб-интерфейса, основанную наC#, а не наJavaScript.С помощью Blazor вы можете писать собственную клиентскую логику и компоненты пользовательского интерфейса наC#, компилировать их в обычные сборки.NET, а затем запускать их непосредственно в браузере с помощью нового открытого веб-стандарта WebAssembly.

Кроме того, Blazor позволяет запускать интерфейсные компоненты.NET на сервере и гибко управлять всеми взаимодействиями с интерфейсом через подключение к браузеру в режиме реального времени.При использовании со средой.NET, работающей на сервере, Blazor позволяет разрабатывать на.NET веб-приложения для всего стека.Хотя платформа Blazor имеет много общего с ASP.NET WebForms, например многократно используемую модель компонентов и простую обработку пользовательских событий, она также использует основы .NETCore для создания современной и высокопроизводительной среды веб-разработки.

Эта книга знакомит разработчиков ASP.NET WebForms с Blazor в привычном и удобном для них формате.Понятия Blazor вводятся здесь в сравнении с аналогичными понятиями из ASP.NET WebForms, и дается пояснение новых понятий, которые могут оказаться менее знакомыми.Книга охватывает обширный спектр вопросов и проблем, включая создание компонентов, маршрутизацию, макеты, настройку и безопасность.И хотя она в первую очередь помогает делать новые разработки, в ней также приведены рекомендации и стратегии по модернизации и переносу на Blazor уже существующих приложений на базе ASP.NET WebForms.

Для кого предназначена эта книга


Книга предназначена для разработчиков ASP.NET WebForms, желающих познакомиться с Blazor в контексте имеющихся у них знаний и навыков.Она поможет быстро приступить к работе над новым Blazor-проектом или составить план модернизации существующего приложения на основе ASP.NET WebForms.

Как пользоваться книгой


В первой части книги приведено описание платформы Blazor и сравнение разработки на этой платформе с разработкой веб-приложений на базе ASP.NET WebForms.Затем в отдельных главах книги описываются различные аспекты Blazor, причем каждое понятие Blazor соотносится с аналогичным понятием в ASP.NET WebForms либо дается полное пояснение для принципиально новых концепций.Книга также регулярно ссылается на образец полноценного приложения, реализованного как на базе ASP.NET WebForms, так и на платформе Blazor, чтобы продемонстрировать функции Blazor и представить пример миграции на Blazor с ASP.NET WebForms.Обе реализации образца приложения (версии для ASP.NET WebForms и Blazor) можно найти наGitHub.

Темы, которые выходят за рамки этой книги


Эта книга представляет собой введение в Blazor, а не исчерпывающее руководство по миграции.Хотя она содержит рекомендации по миграции проекта с ASP.NET WebForms на Blazor, она не стремится описывать все нюансы и детали.Общие рекомендации по переходу с ASP.NET на ASP.NET Core см. вруководстве по миграциив документации по ASP.NET Core.

Дополнительные ресурсы


Официальную домашнюю страницу Blazor и документацию по этой платформе можно найти по адресуhttps://blazor.net.

Отправить отзыв


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

Ссылки на книгу


Подробнее..

Обновления ASP.NET Core в .NET 6 Preview 1

25.02.2021 10:21:06 | Автор: admin

Новая версия .NET, 6 Preview 1, уже доступна и готова к вашей оценке. Это первая предварительная версия .NET 6, следующего крупного обновления платформы .NET. Ожидается, что .NET 6 поступит в полноценный доступ в ноябре этого года и будет выпуском с долгосрочной поддержкой (LTS).

Если вы работаете с Windows и используете Visual Studio, мы рекомендуем установить последнюю предварительную версию Visual Studio 2019 16.9. Если вы используете macOS, мы рекомендуем установить последнюю предварительную версию Visual Studio 2019 для Mac 8.9.

Основная работа, запланированная с ASP.NET Core в .NET 6

.NET 6 использует открытый процесс планирования, поэтому вы можете изучить все основные темы, запланированные для этого релиза, на Blazor-веб-сайте themesof.net. В дополнение к этим верхнеуровневым темам мы собираемся также предоставить множество улучшений, ориентированных на пользователей. Вы можете найти список основных задач, запланированных для ASP.NET Core в .NET 6, в нашем выпуске дорожной карты. Вот некоторые из основных функций ASP.NET Core, запланированных для выпуска .NET 6:

Мы приветствуем отзывы и участие в процессе планирования и создания на GitHub.

Что нового в ASP.NET Core в .NET 6 Preview 1?

  • Поддержка IAsyncDisposableв MVC

  • DynamicComponent

  • InputElementReferenceразделен на релевантные компоненты

  • dotnet watchтеперь являетсяdotnet watch runпо дефолту

  • Nullable reference type annotations

Начало работы

Чтобы начать работу с ASP.NET Core в .NET 6 Preview 1, установите .NET 6 SDK.

Обновление существующего проекта

Чтобы обновить существующее приложение ASP.NET Core с .NET 5 до .NET 6 Preview 1:

  • Обновите целевую платформу для вашего приложения, доnet6.0.

  • Обновите все ссылки на пакеты Microsoft.AspNetCore.* до6.0.0-preview.1.*.

  • Обновите все ссылки на пакеты Microsoft.Extensions.* до6.0.0-preview.1.*.

См. полный список критических изменений в ASP.NET Core для .NET 6 здесь.

DynamicComponent

DynamicComponent - это новый встроенный компонент Blazor, который можно использовать для динамической визуализации компонента, указанного по типу.

<DynamicComponent Type="@someType" />

Параметры могут быть переданы визуализируемому компоненту с помощью dictionary:

<DynamicComponent Type="@someType" Parameters="@myDictionaryOfParameters" />@code {    Type someType = ...    IDictionary<string, object> myDictionaryOfParameters = ...}

InputElementReferenceразделен на релевантные компоненты

Соответствующие встроенные компоненты Blazor ввода теперь предоставляют удобную ссылку ElementReference для базового ввода, что упрощает распространенные сценарии, такие как установка фокуса пользовательского интерфейса на вводе. Затронутые компоненты: InputCheckbox, InputDate, InputFile, InputNumber, InputSelect, InputText и InputTextArea.

dotnet watchтеперь являетсяdotnet watch runпо дефолту

Запуск dotnet watch теперь будет запускать dotnet watch run по умолчанию, экономя драгоценное время ввода.

Nullable Reference Type Annotations

Мы применяем аннотации обнуляемости к частям ASP.NET Core. Значительное количество новых API было аннотировано в .NET 6 Preview 1.

Используя новую функцию C# 8, ASP.NET Core может обеспечить дополнительную безопасность во время компиляции при обработке ссылочных типов, например защиту от исключений нулевых ссылок. Проекты, которые выбрали использование аннотаций, допускающих значение NULL, могут видеть новые предупреждения во время сборки от API-интерфейсов ASP.NET Core.

Чтобы включить ссылочные типы, допускающие значение NULL, вы можете добавить в файл проекта следующее свойство:

<PropertyGroup>    <Nullable>enable</Nullable></PropertyGroup>

Подробности читайте здесь.

Подробнее..

Перевод Объединяем Blazor и Razor Pages в одном ASP.NET Core 3 приложении

25.08.2020 00:11:37 | Автор: admin
Перевод статьи подготовлен в преддверии старта курса C# ASP.NET Core разработчик.





В этой статье я расскажу, как вы можете добавить страницы на основе Blazor в существующее приложение Razor Pages.



Предисловие


Выход Blazor на золото должен произойти через две недели. Многие вещи в проекте еще подвержены достаточно резким изменениям, и последняя предварительная 9-я версия значительно усложнила взаимодействие между компонентами Razor Pages и Blazor: теперь невозможно передавать параметры из страницы Razor в компонент Blazor с помощью Html.RenderComponentAsync. Это может измениться в будущем, но вполне вероятно, что в .NET Core 3.0 он появится с этим ограничением.

Если вы все же хотите улучшить свое существующее приложение на основе Razor Pages с помощью Blazor-магии, одно из решений полностью создавать свои страницы в Blazor. В этой статье я покажу, как вы можете добавлять Blazor-страницы в существующее приложение на основе Razor Pages, где одни части приложения создаются с помощью Razor Pages, а другие с помощью Blazor Pages. Для обоих типов страниц используется одинаковый макет.

Шаг первый: поддержка Blazor


Итак, у нас есть уже существующее Razor Pages приложение, которое было преобразовано в .NET Core 3.



Для начала, вам нужно добавить в свое приложение поддержку Blazor. Эта поддержка позволит вам отображать Blazor-компоненты с Razor-страницы. Официальная документация полностью проводит вас по этому процессу, но вот его краткое изложение.

Startup.cs:

Нам необходимо добавить Services.AddServerSideBlazor в ConfigureServices и endpoints.MapBlazorHub в Configure:



_Layout.cshtml:

JS-библиотека Blazor необходима для подключения Blazor на стороне сервера. Она может быть добавлена в _Layout.cshtml:



?
<script src="_framework/blazor.server.js"></script>

_Imports.razor:

Нам также потребуется новый файл с именем _Imports.razor. Он должен быть добавлен в папку Pages:



_Imports.razor используется для установки using-выражений для ваших Blazor-компонентов. Начать можно со следующего:

?
@using System.Net.Http@using Microsoft.AspNetCore.Components.Forms@using Microsoft.AspNetCore.Components.Routing@using Microsoft.JSInterop@using Microsoft.AspNetCore.Components.Web

И на этом все. Теперь наше приложение поддерживает Blazor. Мы можем проверить это, скопировав классический компонент Counter (счетчик) в наше приложение



?
@page "/counter" <h1>Counter</h1><p>Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code {    int currentCount = 0;     void IncrementCount()    {        currentCount++;    }}

И отредактируем Privacy.cshtml, чтобы включить компонент Counter:

<a href="http://personeltest.ru/aways/mikaelkoskinen.net/post/combining-razor-blazor-pages-single-asp-net-core-3-application#">?</a>@page@model PrivacyModel@{    ViewData["Title"] = "Privacy Policy";}<h1>@ViewData["Title"]</h1> <p>Use this page to detail your site's privacy policy.</p> <component>@(await Html.RenderComponentAsync<Counter>(RenderMode.Server))</component>

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



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

Шаг второй: поддержка Blazor Pages


Наш Blazor-компонент определяет маршрут /counter:



Но переход по нему не работает:



Наша цель заставить работать маршрутизацию на Blazor-страницы. И мы хотим, чтобы Blazor-страницы использовали тот же макет, что и Razor-страницы. Для этого нам понадобится несколько вещей, начиная с маршрутизатора (Router).

App.razor:

Создайте новый файл App.razor в папке
Pages
:



Компонент Router определен в App.razor:

?

@using Microsoft.AspNetCore.Components.Routing <Router AppAssembly="typeof(Program).Assembly">    <Found Context="routeData">        <RouteView RouteData="routeData"/>    </Found>    <NotFound>        <h1>Page not found</h1>        <p>Sorry, but there's nothing here!</p>    </NotFound></Router>

Router автоматически просматривает все Blazor-компоненты с помощью page-директивы и добавляет к ним маршруты.

_Host.cshtml:

Нам также нужна страница, которая будет использоваться как хост для Blazor-страниц. Ее можно назвать как угодно, но в шаблонах Blazor по умолчанию используется _Host.cshtml, которое вполне нас устроит (впрочем, как и любое другое). В _Host.cshtml мы можем определить макет, который в нашем случае будет таким же, как у Razor-страниц.



_Host.cshtml содержит вызов Html.RenderComponentAsync:

?

@page "/blazor" @{    Layout = "_Layout";} <app>    @(await Html.RenderComponentAsync<App>(RenderMode.Server))</app>

Startup.cs:

И, наконец, небольшое дополнение к методу Configure Startup.cs. Ранее мы добавляли MapBlazorHub, а теперь нам нужно добавить вызов MapFallbackToPage, указывающий на новый _Host.cshtml:



И на этом все! Теперь нам просто нужно протестировать наш сетап. Добавьте счетчик страниц Blazor (Counter) в свой макет, отредактировав Pages/Shared/_Layout.cshtml:



Когда мы запускаем приложение, мы видим рабочую Blazor-страницу в нашем Razor Pages приложении:



И мы не ломали поддержку добавления Blazor-компонентов в Razor Pages:



Примечания


Следует отметить пару моментов:

  • Маршруты Blazor работают только тогда, когда они указывают на корень. Если /counter изменить, например, на /products/counter, страница не сможет загрузить требуемый blazor.server.js. Вместо этого мы получим 404. Должна быть возможность изменить тег script, чтобы он мог загружать требуемый скрипт независимо от локации, но, похоже, это изменилось с предварительной 8-й версии в предварительной 9-й, и я не смог заставить его работать. Вот скриншот 404, показывающий проблему:
  • Если вам удалось загрузить скрипт, вы, вероятно, столкнетесь с теми же проблемами с Blazor hub: скрипты пытаются найти hub в /products/blazor вместо blazor. Чтобы обойти это, вы можете вручную запускать соединение между сервером и браузером:

?

<script src="~/_framework/blazor.server.js" autostart="false"></script><script>  Blazor.start({    configureSignalR: function (builder) {      builder.withUrl('/_blazor');    }  });</script>

Пример кода


Пример кода для этого проекта доступен на GitHub.



Хотите узнать о нашем курсе подробнее? Вам сюда.



Читать ещё:




Подробнее..

Варианты использования конфигурации в ASP.NET Core

08.09.2020 02:17:37 | Автор: admin
Для получения конфигурации приложения обычно используют метод доступа по ключевому слову (ключ-значение). Но это бывает не всегда удобно т.к. иногда требуется использовать готовые объекты в коде с уже установленными значениями, причем с возможностью обновления значений без перезагрузки приложения. В данном примере предлагается шаблон использования конфигурации в качестве промежуточного слоя для ASP.NET Core приложений.

Предварительно рекомендуется ознакомиться с материалом: Metanit Конфигурация, Как работает конфигурация в .NET Core.

Постановка задачи


Необходимо реализовать ASP NET Core приложение с возможностью обновления конфигурации в формате JSON во время работы. Во время обновления конфигурации текущие работающие сессии должны продолжать работать с предыдущим вариантов конфигурации. После обновления конфигурации, используемые объекты должны быть обновлены/заменены новыми. Конфигурация должна быть десериализована, не должно быть прямого доступа к объектам IConfiguration из контроллеров. Считываемые значения должны проходить проверку на корректность, при отсутствии как таковых заменяться значениями по умолчанию. Реализация должна работать в Docker контейнере.

Классическая работа с конфигурацией


GitHub: ConfigurationTemplate_1
Проект основан на шаблоне ASP NET Core MVC. Для работы с файлами конфигурации JSON используется провайдер конфигурации JsonConfigurationProvider. Для добавления возможности перезагрузки конфигурации приложения, во время работы, добавим параметр: reloadOnChange: true.
В файле Startup.cs заменим:
public Startup(IConfiguration configuration) {   Configuration = configuration; }

На
public Startup(IConfiguration configuration) {            var builder = new ConfigurationBuilder()    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);   configuration = builder.Build();   Configuration = configuration;  }

.AddJsonFile добавляет JSON файл, reloadOnChange:true указывает на то, что при изменение параметров файла конфигурации, они будут перезагружены без необходимости перезагружать приложение.
Содержимое файла appsettings.json:
{  "AppSettings": {    "Parameter1": "Parameter1 ABC",    "Parameter2": "Parameter2 ABC"    },  "Logging": {    "LogLevel": {      "Default": "Information",      "Microsoft": "Warning",      "Microsoft.Hosting.Lifetime": "Information"    }  },  "AllowedHosts": "*"}

Контроллеры приложения вместо прямого обращения к конфигурации будут использовать сервис: ServiceABC. ServiceABC класс который первоначальные значения берет из файла конфигурации. В данном примере класс ServiceABC содержит только одно свойство Title.
Содержимое файла ServiceABC.cs:
public class ServiceABC{  public string Title;  public ServiceABC(string title)  {     Title = title;  }  public ServiceABC()  { }}

Для использования ServiceABC необходимо его добавить в качестве сервиса middleware в приложение. Добавим сервис как AddTransient, который создается каждый раз при обращении к нему, с помощью выражения:
services.AddTransient<IYourService>(o => new YourService(param));
Отлично подходит для легких сервисов, не потребляющих память и ресурсы. Чтение параметров конфигурации в Startup.cs осуществляется с помощью IConfiguration, где используется строка запроса с указанием полного пути расположения значения, пример: AppSettings:Parameter1.
В файле Startup.cs добавим:
public void ConfigureServices(IServiceCollection services){  //Считывание параметра "Parameter1" для инициализации сервиса ServiceABC  var settingsParameter1 = Configuration["AppSettings:Parameter1"];  //Добавление сервиса "Parameter1"              services.AddScoped(s=> new ServiceABC(settingsParameter1));  //next  services.AddControllersWithViews();}

Пример использования сервиса ServiceABC в контроллере, значение Parameter1 будет отображаться на html странице.
Для использования сервиса в контроллерах добавим его в конструктор, файл HomeController.cs
public class HomeController : Controller{  private readonly ILogger<HomeController> _logger;  private readonly ServiceABC _serviceABC;  public HomeController(ILogger<HomeController> logger, ServiceABC serviceABC)    {      _logger = logger;      _serviceABC = serviceABC;    }  public IActionResult Index()    {      return View(_serviceABC);    }

Добавим видимость сервиса ServiceABC в файл _ViewImports.cshtml
@using ConfigurationTemplate_1.Services

Изменим Index.cshtml для отображение параметра Parameter1 на странице.
@model ServiceABC@{    ViewData["Title"] = "Home Page";}    <div class="text-center">        <h1>Десериализация конфигурации в ASP.NET Core</h1>        <h4>Классическая работа с конфигурацией</h4>    </div><div>            <p>Сервис ServiceABC, отображение Заголовка        из параметра Parameter1 = @Model.Title</p></div>

Запустим приложение:


Итог


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

Использование IConfiguration как Singleton


GitHub: ConfigurationTemplate_2
Второй вариант заключается в помещение IConfiguration(как Singleton) в сервисы. В результате IConfiguration может вызываться из контроллеров и других сервисов. При использовании AddSingleton сервис создается один раз и при использовании приложения обращение идет к одному и тому же экземпляру. Использовать этот способ нужно особенно осторожно, так как возможны утечки памяти и проблемы с многопоточностью.
Заменим код из предыдущего примера в Startup.cs на новый, где
services.AddSingleton<IConfiguration>(Configuration);
добавляет IConfiguration как Singleton в сервисы.
public void ConfigureServices(IServiceCollection services){  //Доступ к IConfiguration из других контроллеров и сервисов  services.AddSingleton<IConfiguration>(Configuration);  //Добавление сервиса "ServiceABC"                            services.AddScoped<ServiceABC>();  //next  services.AddControllersWithViews();}

Изменим конструктор сервиса ServiceABC для принятия IConfiguration
public class ServiceABC{          private readonly IConfiguration _configuration;  public string Title => _configuration["AppSettings:Parameter1"];          public ServiceABC(IConfiguration Configuration)    {      _configuration = Configuration;    }  public ServiceABC()    { }}

Как и в предыдущем варианте добавим сервис в конструктор и добавим ссылку на пространство имен
Для использования сервиса в контроллерах добавим его в конструктор, файл HomeController.cs
public class HomeController : Controller{  private readonly ILogger<HomeController> _logger;  private readonly ServiceABC _serviceABC;  public HomeController(ILogger<HomeController> logger, ServiceABC serviceABC)    {      _logger = logger;      _serviceABC = serviceABC;    }  public IActionResult Index()    {      return View(_serviceABC);    }

Добавим видимость сервиса ServiceABC в файл _ViewImports.cshtml:
@using ConfigurationTemplate_2.Services;

Изменим Index.cshtml для отображения параметра Parameter1 на странице.
@model ServiceABC@{    ViewData["Title"] = "Home Page";}<div class="text-center">    <h1>Десериализация конфигурации в ASP.NET Core</h1>    <h4>Использование IConfiguration как Singleton</h4></div><div>    <p>        Сервис ServiceABC, отображение Заголовка        из параметра Parameter1 = @Model.Title    </p></div>


Запустим приложение:


Сервис ServiceABC добавленный в контейнер с помощью AddScoped означает, что экземпляр класса будет создаваться при каждом запросе страницы. В результате экземпляр класса ServiceABC будет создаваться при каждом http запросе вместе с перезагрузкой конфигурации IConfiguration, и новые изменения в appsettings.json будут применяться.
Таким образом, если во время работы приложения изменить параметр Parameter1 на NEW!!! Parameter1 ABC, то при следующем обращение к начальной странице отобразится новое значение параметра.

Обновим страницу после изменения файла appsettings.json:


Итог


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

Десериализация конфигурации с валидацией (вариант IOptions)


GitHub: ConfigurationTemplate_3
Почитать про Options по ссылке.
В этом варианте необходимость использования ServiceABC отпадает. Вместо него используется класс AppSettings, который содержит параметры из конфигурационного файла и объект ClientConfig. Объект ClientConfig после изменения конфигурации требуется инициализировать, т.к. в контроллерах используется готовый объект. ClientConfig это некий класс, взаимодействующий с внешними системами, код которого нельзя изменять. Если выполнить только десериализацию данных класса AppSettings, то ClientConfig будет в состояние null. Поэтому необходимо подписаться на событие чтения конфигурации, и в обработчике инициализировать объект ClientConfig.
Для передачи конфигурации не в виде пар ключ-значение, а как объекты определенных классов, будем использовать интерфейс IOptions. Дополнительно IOptions в отличие от ConfigurationManager позволяет десерилизовать отдельные секции. Для создания объекта ClientConfig потребуется использовать IPostConfigureOptions, который выполняется после обработки всех конфигурации. IPostConfigureOptions будет выполняться каждый раз после чтения конфигурации, самым последним.
Создадим ClientConfig.cs:
public class ClientConfig{  private string _parameter1;  private string _parameter2;  public string Value => _parameter1 + " " + _parameter2;  public ClientConfig(ClientConfigOptions configOptions)    {      _parameter1 = configOptions.Parameter1;      _parameter2 = configOptions.Parameter2;    }}

В качестве конструктора будет принимать параметры в виде объекта ClientConfigOptions:
public class ClientConfigOptions{  public string Parameter1;  public string Parameter2;} 

Создадим класс настроек AppSettings, и определим в нем метод ClientConfigBuild(), который создаст объект ClientConfig.
Файл AppSettings.cs:
public class AppSettings{          public string Parameter1 { get; set; }  public string Parameter2 { get; set; }          public ClientConfig clientConfig;  public void ClientConfigBuild()    {      clientConfig = new ClientConfig(new ClientConfigOptions()        {          Parameter1 = this.Parameter1,          Parameter2 = this.Parameter2        }        );      }}

Создадим обработчик конфигурации, который будет отрабатываться последним. Для этого он должен быть унаследован от IPostConfigureOptions. Вызываемый последним метод PostConfigure выполнит ClientConfigBuild(), который как раз и создаст ClientConfig.
Файл ConfigureAppSettingsOptions.cs:
public class ConfigureAppSettingsOptions: IPostConfigureOptions<AppSettings>{  public ConfigureAppSettingsOptions()    { }  public void PostConfigure(string name, AppSettings options)    {                  options.ClientConfigBuild();    }}

Теперь осталось внести изменения только в Startup.cs, изменения коснутся только функции ConfigureServices(IServiceCollection services).
Сначала прочитаем секцию AppSettings в appsettings.json
// configure strongly typed settings objectsvar appSettingsSection = Configuration.GetSection("AppSettings");services.Configure<AppSettings>(appSettingsSection);

Далее, для каждого запроса будет создаваться копия AppSettings для возможности вызова постобработки:
services.AddScoped(sp => sp.GetService<IOptionsSnapshot<AppSettings>>().Value);

Добавим в качестве сервиса, постобработку класса AppSettings:
services.AddSingleton<IPostConfigureOptions<AppSettings>, ConfigureAppSettingsOptions>();

Добавленный код в Startup.cs
public void ConfigureServices(IServiceCollection services){  // configure strongly typed settings objects  var appSettingsSection = Configuration.GetSection("AppSettings");  services.Configure<AppSettings>(appSettingsSection);  services.AddScoped(sp => sp.GetService<IOptionsSnapshot<AppSettings>>().Value);                                      services.AddSingleton<IPostConfigureOptions<AppSettings>, ConfigureAppSettingsOptions>();              //next  services.AddControllersWithViews();}

Для получения доступа к конфигурации, из контроллера достаточно будет просто внедрить AppSettings.
Файл HomeController.cs:
public class HomeController : Controller{  private readonly ILogger<HomeController> _logger;  private readonly AppSettings _appSettings;  public HomeController(ILogger<HomeController> logger, AppSettings appSettings)    {      _logger = logger;      _appSettings = appSettings;    }

Изменим Index.cshtml для вывода параметра Value объекта СlientConfig
@model AppSettings@{    ViewData["Title"] = "Home Page";}<div class="text-center">    <h1>Десериализация конфигурации в ASP.NET Core</h1>    <h4>Десериализация конфигурации с валидацией (вариант IOptions)</h4></div><div>    <p>        Класс ClientConfig, отображение Заголовка         = @Model.clientConfig.Value    </p></div>

Запустим приложение:


Если во время работы приложения изменить параметр Parameter1 на NEW!!! Parameter1 ABC и Parameter2 на NEW!!! Parameter2 ABC, то при следующем обращении к начальной странице отобразится новое свойства Value:



Итог


Данный подход позволяет десериализовать все значения конфигурации без ручного перебирания параметров. Каждый запрос http работает со своим экземпляром AppSettings и СlientConfig, что исключает ситуацию возникновения коллизий. IPostConfigureOptions гарантирует выполнение в последнюю очередь, когда все параметры будут перечитаны. Недостатком решения является постоянное создание экземпляра СlientConfig для каждого запроса, что является непрактичным т.к. по сути СlientConfig должен пересоздаваться только после изменения конфигурации.

Десериализация конфигурации с валидацией (без использования IOptions)


GitHub: ConfigurationTemplate_4
Использование подхода с использованием IPostConfigureOptions приводит к созданию объекта ClientConfig каждый раз при получении запроса от клиента. Это недостаточно рационально т.к. каждый запрос работает с начальным состоянием ClientConfig, которое меняется только при изменение конфигурационного файла appsettings.json. Для этого откажемся от IPostConfigureOptions и создадим обработчик конфигурации который будет вызваться только при изменении appsettings.json, в результате ClientConfig будет создаваться только один раз, и далее на каждый запрос будет отдаваться уже созданный экземпляр ClientConfig.
Создадим класс SingletonAppSettings конфигурации(Singleton) с которого будет создаваться экземпляр настроек для каждого запроса.
Файл SingletonAppSettings.cs:
public class SingletonAppSettings{  public AppSettings appSettings;    private static readonly Lazy<SingletonAppSettings> lazy = new Lazy<SingletonAppSettings>(() => new SingletonAppSettings());  private SingletonAppSettings()    { }  public static SingletonAppSettings Instance => lazy.Value;}

Вернемся в класс Startup и добавим ссылку на интерфейс IServiceCollection. Он будет использоваться в методе обработки конфигурации
public IServiceCollection Services { get; set; }

Изменим ConfigureServices(IServiceCollection services) и передадим ссылку на IServiceCollection:
Файл Startup.cs:
public void ConfigureServices(IServiceCollection services){  Services = services;  //Считаем секцию AppSettings из конфигурации  var appSettings = Configuration.GetSection("AppSettings").Get<AppSettings>();  appSettings.ClientConfigBuild();

Создадим Singleton конфигурации, и добавим его в коллекцию сервисов:
SingletonAppSettings singletonAppSettings = SingletonAppSettings.Instance;singletonAppSettings.appSettings = appSettings;services.AddSingleton(singletonAppSettings);     

Добавим объект AppSettings как Scoped, при каждом запросе будет создаваться копия от Singleton:
services.AddScoped(sp => sp.GetService<SingletonAppSettings>().appSettings);

Полностью ConfigureServices(IServiceCollection services):
public void ConfigureServices(IServiceCollection services){  Services = services;  //Считаем секцию AppSettings из конфигурации  var appSettings = Configuration.GetSection("AppSettings").Get<AppSettings>();  appSettings.ClientConfigBuild();  SingletonAppSettings singletonAppSettings = SingletonAppSettings.Instance;  singletonAppSettings.appSettings = appSettings;  services.AddSingleton(singletonAppSettings);               services.AddScoped(sp => sp.GetService<SingletonAppSettings>().appSettings);  //next  services.AddControllersWithViews();}

Теперь добавить обработчик для конфигурации в Configure(IApplicationBuilder app, IWebHostEnvironment env). Для отслеживания изменения в файле appsettings.json используется токен. OnChange вызываемая функция при изменении файла. Обработчик конфигурации onChange():
ChangeToken.OnChange(() => Configuration.GetReloadToken(), onChange);

Вначале читаем файл appsettings.json и десериализуем класс AppSettings. Затем из коллекции сервисов получаем ссылку на Singleton, который хранит объект AppSettings, и заменяем его новым.
private void onChange(){                          var newAppSettings = Configuration.GetSection("AppSettings").Get<AppSettings>();  newAppSettings.ClientConfigBuild();  var serviceAppSettings = Services.BuildServiceProvider().GetService<SingletonAppSettings>();  serviceAppSettings.appSettings = newAppSettings;  Console.WriteLine($"AppSettings has been changed! {DateTime.Now}");}

В контроллер HomeController внедрим ссылку на AppSettings, как в предыдущем варианте (ConfigurationTemplate_3)
Файл HomeController.cs:
public class HomeController : Controller{  private readonly ILogger<HomeController> _logger;  private readonly AppSettings _appSettings;  public HomeController(ILogger<HomeController> logger, AppSettings appSettings)    {      _logger = logger;      _appSettings = appSettings;    }

Изменим Index.cshtml для вывода параметра Value объекта СlientConfig:
@model AppSettings@{    ViewData["Title"] = "Home Page";}<div class="text-center">    <h1>Десериализация конфигурации в ASP.NET Core</h1>    <h4>Десериализация конфигурации с валидацией (без использования IOptions)</h4></div><div>    <p>        Класс ClientConfig, отображение Заголовка        = @Model.clientConfig.Value    </p></div>


Запустим приложение:


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


И новые значения:


Итог


Данный вариант лучше использования IPostConfigureOptions т.к. позволяет строить объект только после изменения файла конфигурации, а не при каждом запросе. В результате достигается уменьшение времени на ответ сервера. После срабатывания токена, состояние токена сбрасывается.

Добавление значений по умолчанию и валидация конфигурации


GitHub: ConfigurationTemplate_5
В предыдущих примерах при отсутствии файла appsettings.json приложение выбросит исключение, поэтому сделаем файл конфигурации опциональным и добавим настройки по умолчанию. При публикации приложения проекта, созданного из шаблона в Visula Studio, файл appsettings.json будет располагаться в одной и той же папке вместе со всеми бинарными файлами, что неудобно при развертывание в Docker. Файл appsettings.json перенесем в папку config/:
.AddJsonFile("config/appsettings.json")

Для возможности запуска приложения без appsettings.json изменим параметр optional на true, который в данном случае означает, что наличие appsettings.json является необязательным.
Файл Startup.cs:
public Startup(IConfiguration configuration){  var builder = new ConfigurationBuilder()     .AddJsonFile("config/appsettings.json", optional: true, reloadOnChange: true);  configuration = builder.Build();  Configuration = configuration;}

Добавим в public void ConfigureServices(IServiceCollection services) к строке десериализации конфигурации случай обработки отсутствия файла appsettings.json:
 var appSettings = Configuration.GetSection("AppSettings").Get<AppSettings>() ?? new AppSettings();

Добавим валидацию конфигурации, на основе интерфейса IValidatableObject. При отсутствующих параметрах конфигурации, будет применяться значение по умолчанию. Наследуем класс AppSettings от IValidatableObject и реализуем метод:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)

Файл AppSettings.cs:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext){  List<ValidationResult> errors = new List<ValidationResult>();  if (string.IsNullOrWhiteSpace(this.Parameter1))    {      errors.Add(new ValidationResult("Не указан параметр Parameter1. Задано " +        "значение по умолчанию DefaultParameter1 ABC"));      this.Parameter1 = "DefaultParameter1 ABC";    }    if (string.IsNullOrWhiteSpace(this.Parameter2))    {      errors.Add(new ValidationResult("Не указан параметр Parameter2. Задано " +        "значение по умолчанию DefaultParameter2 ABC"));      this.Parameter2 = "DefaultParameter2 ABC";    }    return errors;}

Добавим метод вызова проверки конфигурации для вызова из класса Startup
Файл Startup.cs:
private void ValidateAppSettings(AppSettings appSettings){  var resultsValidation = new List<ValidationResult>();  var context = new ValidationContext(appSettings);  if (!Validator.TryValidateObject(appSettings, context, resultsValidation, true))    {      resultsValidation.ForEach(        error => Console.WriteLine($"Проверка конфигурации: {error.ErrorMessage}"));      }    }

Добавим вызов метода валидации конфигурации в ConfigureServices(IServiceCollection services). Если файла appsettings.json отсутствует, то требуется инициализировать объект AppSettings со значениями по умолчанию.
Файл Startup.cs:
var appSettings = Configuration.GetSection("AppSettings").Get<AppSettings>() ?? new AppSettings();

Проверка параметров. В случае использования значения по умолчанию в консоль будет выведено сообщение с указанием параметра.
 //Validate            this.ValidateAppSettings(appSettings);            appSettings.ClientConfigBuild();

Изменим проверку конфигурации в onChange()
private void onChange(){                          var newAppSettings = Configuration.GetSection("AppSettings").Get<AppSettings>() ?? new AppSettings();  //Validate              this.ValidateAppSettings(newAppSettings);              newAppSettings.ClientConfigBuild();  var serviceAppSettings = Services.BuildServiceProvider().GetService<SingletonAppSettings>();  serviceAppSettings.appSettings = newAppSettings;  Console.WriteLine($"AppSettings has been changed! {DateTime.Now}");}

Если из файла appsettings.json удалить ключ Parameter1, то после сохранения файла в окне консольного приложения появится сообщение об отсутствие параметра:


Итог


Изменение пути для расположения конфигураций в папке config является хорошим решением т.к. позволяет не смешивать все файлы в одну кучу. Папка config определяется только для хранения конфигурационных файлов. Упростили задачу развертывания и конфигурирования приложения для администраторов, благодаря валидации конфигурации. Если добавить вывод ошибок конфигурации в лог, то администратор в случае указания неправильных параметров, получит точную информацию о проблеме, а не как в последнее время программисты на любое исключение стали писать: Что -то пошло не так.
Идеального варианта работы с конфигурацией не существует, все зависит от поставленной задачи, у каждого варианта есть свои плюсы и минусы.

Все шаблоны конфигураций доступны по ссылке.

Литература:
  1. Корректный ASP.NET Core
  2. METANIT Конфигурация. Основы конфигурации
  3. Singleton Design Pattern C# .net core
  4. Reloading configuration in .NET core
  5. Reloading strongly typed Options on file changes in ASP.NET Core RC2
  6. Конфигурация ASP.NET Core приложения через IOptions
  7. METANIT Передача конфигурации через IOptions
  8. Конфигурация ASP.NET Core приложения через IOptions
  9. METANIT Самовалидация модели
Подробнее..
Категории: C , Net , Net core , Configuration , Asp , Asp.net

Перевод Локализация в ASP.NET Core Razor Pages Культуры

26.10.2020 20:10:07 | Автор: admin

Привет, хабр! Прямо сейчас OTUS открывает набор на новый поток курса "C# ASP.NET Core разработчик". В связи с этим традиционно делимся с вами полезным переводом и приглашаем записаться на день открытых дверей, в рамках которого можно будет подробно узнать о курсе, а также задать эксперту интересующие вас вопросы.


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

Глобализация в ASP.NET Core

Глобализация - это подготовка приложения к поддержке различных языков в зависимости от предпочтений пользователя. Локализация - это процесс адаптации контента сайта под разные страны, регионы или культуры. Отправной точкой в глобализации веб-приложения является способность определять язык или культуру для каждого запроса. Далее нам потребуется механизм для выбора контента на основе культуры текущего запроса. В этой статье мы рассмотрим роль, которую играет класс CultureInfo в локализации, и то, как можно реализовать компонент представления, чтобы пользователи могли выбирать для запросов предпочтительную для них культуру.

Приведенные ниже шаги добавляют базовую локализацию к Razor Pages приложению, которое создается из стандартного шаблона веб-приложения ASP.NET Core 3.0 без настроенной аутентификации. Я назвал свое приложение Localisation. Свое вы можете назвать как вам угодно, но в таком случае не забудьте про пространства имен, если будете копировать код из этой статьи.

1. Начните с открытия файла Startup.cs и добавления туда следующих using директив:

using System.Globalization;using Microsoft.AspNetCore.Localization;using Microsoft.Extensions.Options;

2. Локализация - это дополнительная фича. По умолчанию она не включена. Измените метод ConfigureServices, включив AddLocalization, что сделает различные вспомогательные сервисы локализации доступными для системы инжекции зависимостей. Затем добавьте следующий код для конфигурации RequestLocalizationOptions в приложении.

services.Configure<RequestLocalizationOptions>(options =>{   var supportedCultures = new[]    {        new CultureInfo("en"),        new CultureInfo("de"),        new CultureInfo("fr"),        new CultureInfo("es"),        new CultureInfo("ru"),        new CultureInfo("ja"),        new CultureInfo("ar"),        new CultureInfo("zh"),        new CultureInfo("en-GB")    };    options.DefaultRequestCulture = new RequestCulture("en-GB");    options.SupportedCultures = supportedCultures;    options.SupportedUICultures = supportedCultures;});

Вам необходимо указать языки или культуры, которые вы планируете поддерживать в своем приложении. Культуры представлены в .NET классом CultureInfo, который содержит информацию о форматировании чисел и дат, календарях, системах письма, порядках сортировки и других вопросах, зависящих от местности проживания конечного пользователя. Перегрузка конструктора класса CultureInfo, используемого здесь, принимает строку, представляющую имя языка и региональных параметров (культуры). Допустимые значения - это коды ISO 639-1, которые представляют язык (например, en для английского языка), с необязательным кодом субкультуры ISO 3166, который представляет страну или диалект (например, en-GB для Великобритании или en-ZA для Южной Африки). В приведенном выше примере поддерживается несколько языков, включая одну субкультуру - британский английский, который был установлен в качестве культуры по умолчанию.

3. Теперь, когда RequestLocalizationOptions настроены, их можно применить к промежуточной прослойке локализации запросов, которую необходимо добавить в метод Configure после app.UseRouting():

app.UseHttpsRedirection();app.UseStaticFiles();app.UseRouting();var localizationOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>().Value;app.UseRequestLocalization(localizationOptions);

Установка культуры запроса

Для определения культуры текущего запроса промежуточный слой локализации запросов используются компоненты, называемые RequestCultureProviders. Три из них добавляются по умолчанию:

  • QueryStringRequestCultureProvider, который получает культуру из строки запроса

  • CookieRequestCultureProvider, который получает культуру из файла cookie

Провайдеры вызываются один за другим до тех пор, пока не удастся определить культуру запроса. Также можно создать своего собственного провайдера, на чем я планирую остановиться поподробнее в следующей статье. А пока я покажу, как создать компонент представления, который позволяет пользователю установить культуру для текущего запроса.

1. Первый шаг - создать папку с именем Models и добавить в нее файл класса с именем CultureSwitcherModel.cs.

using System.Collections.Generic;using System.Globalization; namespace Localisation.Models{    public class CultureSwitcherModel    {        public CultureInfo CurrentUICulture { get; set; }        public List<CultureInfo> SupportedCultures { get; set; }    }}

2. Добавьте в проект папку с именем ViewComponents и в нее добавьте новый файл класса C# с именем CultureSwitcherViewcomponent.cs. Затем замените содержимое на следующий код:

using Localisation.Models;using Microsoft.AspNetCore.Builder;using Microsoft.AspNetCore.Localization;using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Options;using System.Linq; namespace Localisation.ViewComponents{    public class CultureSwitcherViewComponent : ViewComponent    {        private readonly IOptions<RequestLocalizationOptions> localizationOptions;        public CultureSwitcherViewComponent(IOptions<RequestLocalizationOptions> localizationOptions) =>            this.localizationOptions = localizationOptions;         public IViewComponentResult Invoke()        {            var cultureFeature = HttpContext.Features.Get<IRequestCultureFeature>();            var model = new CultureSwitcherModel            {                SupportedCultures = localizationOptions.Value.SupportedUICultures.ToList(),                CurrentUICulture = cultureFeature.RequestCulture.UICulture            };            return View(model);        }    }}

3. Добавьте новую папку в папку Pages и назовите ее Components. Внутри добавьте еще одну папку с именем CultureSwitcher. Затем добавьте Razor View в default.cshtml и замените существующее содержимое следующим:

@model CultureSwitcherModel <div>    <form id="culture-switcher">        <select name="culture" id="culture-options">            <option></option>            @foreach (var culture in Model.SupportedCultures)            {                <option value="@culture.Name" selected="@(Model.CurrentUICulture.Name == culture.Name)">@culture.DisplayName</option>            }        </select>    </form></div>  <script>    document.getElementById("culture-options").addEventListener("change", () => {        document.getElementById("culture-switcher").submit();    });</script>

Компонент представления - это простой select элемент, заполненный поддерживаемыми культурами, которые были настроены в Startup. Представление, в котором он находится, использует дефолтный get метод, что означает, что отправленное значение появится в строке запроса с именем culture. QueryStringRequestCultureProvider предназначен для поиска элемента в строке запроса по ключа culture (и/или ui-culture).

CurrentCulture для запроса определяет местное форматирование которое будет использоваться для таких вещей как даты и числа. CurrentUICulture используется, чтобы выбрать правильный ресурс, содержащий переведенные строки. В следующей статье этой серии я рассмотрю, как использовать ресурсные файлы для локализации статического контента. Для CurrentCulture и CurrentUICulture можно установить разные значения, но чаще имеет смысл, чтобы оба эти значения совпадали. Если установлено только одно из них (например, через одно значение строки запроса), то значение присваивается обоим свойствам.

4. На этом этапе у вас, вероятно, у вас появилось несколько красных волнистых линий в только что созданном представлении - откройте файл _ViewImports.cshtml и добавьте вторую и третью директивы using, приведенные ниже, вместе с последней строкой, которая позволяет вам использовать tag-хелпер для рендеринга компонент представления:

@using Localisation@using Localisation.Models@using System.Globalization@namespace Localisation.Pages@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers@addTagHelper *, Localisation

5. Включите компонент переключателя культуры на страницу макета, используя подход с tag-хелпером, как показано здесь в последней строке.

<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">    <ul class="navbar-nav flex-grow-1">        <li class="nav-item">            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>        </li>        <li class="nav-item">            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>        </li>    </ul></div><vc:culture-switcher/>

6. Измените Index.cshtml, включив код в блок кода и HTML-код для таблицы, которая отображает различные биты данных:

@page@using Microsoft.AspNetCore.Localization@model IndexModel@{    ViewData["Title"] = "Home page";    var requestCultureFeature = HttpContext.Features.Get<IRequestCultureFeature>();    var requestCulture = requestCultureFeature.RequestCulture;} <div class="text-center">    <h1 class="display-4">Welcome</h1>    <p>Learn about <a href="http://personeltest.ru/aways/docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>     <table class="table culture-table">        <tr>            <td style="width:50%;">Culture</td>            <td>@requestCulture.Culture.DisplayName {@requestCulture.Culture.Name}</td>        </tr>        <tr>            <td>UI Culture</td>            <td>@requestCulture.UICulture.Name</td>        </tr>        <tr>            <td>UICulture Parent</td>            <td>@requestCulture.UICulture.Parent</td>        </tr>        <tr>            <td>Date</td>            <td>@DateTime.Now.ToLongDateString()</td>        </tr>        <tr>            <td>Currency</td>            <td>                @(12345.00.ToString("c"))            </td>        </tr>        <tr>            <td>Number</td>            <td>                @(123.45m.ToString("F2"))            </td>        </tr>    </table></div>

При первом запуске приложения культура для запроса задается AcceptHeadersCultureRequestProvider. Когда вы используете раскрывающийся список для выбора разных культур, культура задается QueryStringCultureRequestProvider. Попробуйте добавить ключ ui-culture в строку запроса с другим значением ключа culture (например, https://localhost:xxxxx/?culture=es&ui-culture=de), чтобы посмотреть, что произойдет.

Резюме

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

Вы могли заметить, что, хотя названия дней и месяцев переводятся автоматически на основе календаря, связанного с текущей культурой, остальное статическое содержимое остается на английском языке. В следующей статье я покажу, как использовать ресурсные файлы (.resx) для управления несколькими переводами для этого типа контента.


Подробнее о курсе.


Читать ещё:

Подробнее..

Простое и удобное журналирование ошибок для сайтов на .NET Core

29.12.2020 10:06:55 | Автор: admin

Возможно, многим знакома библиотека ELMAH (Error Logging Modules and Handlers), которая позволяет организовать простое журналирование ошибок для любого сайта, созданного с помощью .NET Framework.



Этот простой и проверенный временем инструмент выручал меня во многим проектах.
Несколько лет назад, создавая свой новый проект под .NET Core я с досадой обнаружил, что ELMAH не работает под .NET Core.


Но это же opensource проект! Несколько выходных в работе над форком, и вот готова первая версия ELMAH работающая под .NET Core.


С тех пор много воды утекло, и признаюсь, я несколько забросил свой pet-проект. Однако, сейчас выдалась небольшая передышка между проектами, мне удалось внести существенные улучшения и хочу познакомить уважаемых хабравчан с новой версией ElmahCore.


Итак, ElmahCore это opensource проект, библиотека для регистрации ошибок на сайтах, созданных с помощью технологий .NET Core.
При возникновении необработанного исключения в вашем приложении, библиотека автоматически регистрирует всю имеющуюся информацию об этом факте, включая следующие данные:


  • Тип и информация об исключении, стек вызова
  • Информация об HTTP запросе: данные шапки запроса (header), параметры запроса, cookies, данные о подключении пользователя
  • Информация о текущем пользователе
  • Информация о текущей сессии на сервере
  • Переменные среды сервера

В новой версии я добавил регистрацию всех сообщений, созданных через Microsoft.Extensions.Logging (ILogger) в привязке к контексту HTTP запроса.
Вся эта информация может быть сохранена:


  • в памяти сервера
  • в XML файлах в папке на сервере
  • в СУБД, сейчас поддерживаются MSSQL, MySQL, PostgreSQL

Подключение данной библиотеки максимально простое:


  1. Добавить в проект nuget-пакет elmahcore.
  2. Добавить следующие строчки в Startup.cs:

services.AddElmah(); // в метод ConfigureServices app.UseElmah(); // в начале метода Configure

Для доступа к журналу ошибок библиотека предоставляет программный и пользовательский интерфейс.
Интерфейс пользователя, по умолчанию, доступен по пути ~/elmah.
В новой версии я существенно переработал UI, реализовав его на VUE.js



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


services.AddElmah(options =>{   options.SourcePaths = new []   {      @"D:\tmp\ElmahCore.DemoCore3",      @"D:\tmp\ElmahCore.Mvc",      @"D:\tmp\ElmahCore"   };});

На закладке Log отображается журнал сообщений Microsoft.Extensions.Logging в контексте HTTP запроса в котором была зарегистрирована ошибка.



Понятно, что такая ценная информация не для публичных глаз!
Чтобы защитить доступ к журналу, необходимо добавить проверку прав доступа:


services.AddElmah(options =>{        options.OnPermissionCheck = context => context.User.Identity.IsAuthenticated;});

При этом вызов UseElmah, должен быть позже UseAuthentication и UseAuthorization


app.UseAuthentication();app.UseAuthorization();app.UseElmah();

Можно организовать фильтрацию регистрируемых ошибок с помощь фильтров, реализованных в коде (реализующих интерфейс IErrorFilter) или в xml-файле конфигурации (https://elmah.github.io/a/error-filtering/examples/).


services.AddElmah<XmlFileErrorLog>(options =>{    options.FiltersConfig = "elmah.xml";    options.Filters.Add(new MyFilter());})

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


services.AddElmah<XmlFileErrorLog>(options =>{    options.Notifiers.Add(new ErrorMailNotifier("Email",emailOptions));});

Надеюсь, что эта бесплатная библиотека будет полезна в ваших проектах.
Подробнее с библиотекой можно познакомиться здесь: https://github.com/ElmahCore/ElmahCore

Подробнее..
Категории: C , Open source , Net , Net core , Asp , Asp.net , Error handling , Error_reporting

6 вещей, которые не стоит делать в ASP.NET контроллерах

26.04.2021 02:09:13 | Автор: admin

ASP.NET контроллеры должны быть тонкими

Ох уж эта вечно повторяемая банальность, обросшая тоннами недосказанности.

Почему они должны быть тонкими? Какой в этом плюс? Как сделать их тонкими, если они сейчас не такие? Как сохранить их тонкими?

Правильные (и частые) вопросы. Обсуждение части этих вопросов можно найти в моих ранних статьях, поэтому сейчас мы посмотрим на проблему с другой стороны.

Чтобы начать шаги по превращению наших контроллеров в тонкие, сначала необходимо понять, как контроллеры становятся толстыми.

По моему опыту, есть 6 основных видов кода, проникающих в наши контроллеры, хотя им там вовсе не место. На самом деле этот список не исчерпывающий, и я уверен, что их ещё больше.

1. Маппинг объектов передачи данных (DTO)

Так как наши контроллеры находятся на передовой процесса обработки запроса, часто возникает необходимость создания объектов для запроса и ответа, если нужно получить что-то сложнее, чем просто параметры из адреса, и вернуть не только код ответа HTTP.

Вы понимаете, это что-то вроде:

public IActionResult CheckOutBook([FromBody]BookRequest bookRequest){    var book = new Book();    book.Title = bookRequest.Title;    book.Rating = bookRequest.Rating.ToString();    book.AuthorID = bookRequest.AuthorID;    //...}

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

2. Валидация

Конечно же, мы не можем позволить некорректному вводу пробраться внутрь стен нашего замка, и валидация защищает нас от этого. Сначала на стороне клиента, а затем и на сервере.

Однако, мне нравится относиться к контроллерам как к шеф-поварам. Их ассистенты подготавливают все ингредиенты, поэтому сами они занимаются только финальной компоновкой. И существует множество способов настройки валидаторов в процессе обработки запроса ASP.NET MVC, чтобы контроллеры могли считать запрос валидным и передавать его дальше по цепочке.

Вот такому коду нет оправданий!

public IActionResult Register([FromBody]AutomobileRegistrationRequest request){    // Проверяем, что VIN номер был заполнен...    if (string.IsNullOrEmpty(request.VIN))    {        return BadRequest();    }        //...}

3. Бизнес-логика

Если у вас есть что-то, относящееся к бизнесу в ваших контроллерах, то скорее всего вам понадобится написать то же самое где-то ещё.

Иногда это пересекается и с валидацией тоже. Если в вашей валидации есть правила, продиктованные бизнесом (а не просто проверка принадлежности числа диапазону или наличия строки), то велик риск, что этот код будет повторён в другом месте.

4. Авторизация

Авторизация похожа на валидацию в том плане, что представляет собой защитный барьер. Только в отличие от предотвращения попадания плохих запросов в систему, авторизация не даёт плохим пользователям попасть туда, куда им не следует.

Аналогично в случае с валидацией, ASP.NET предлагает множество путей для выноса авторизации (ПО промежуточного слоя и фильтры, например).

Если вы проверяете свойства объекта Userвнутри контроллера, чтобы разрешить/запретить ему что-то, то, кажется, что у вас есть кое-что для рефакторинга.

5. Обработка ошибок

Это больно, это БОЛЬНО!

public IActionResult GetBookById(int id){    try    {      // Важный код, который должен выполнять шеф-повар...    }    catch (DoesNotExistException)    {      // Код, который должен выполнять ассистент...    }    catch (Exception e)    {      // Пожалуйста, только не это...    }}

Этот пункт довольно обширный, и иногда обработка исключений должна быть сделана в контроллере, но, по моему опыту, почти всегда есть более подходящее, более локализованное место. И если такого места нет, то вы можете воспользоваться преимуществами глобальной обработки исключений на промежуточном слое, чтобы отловить наиболее общие ошибки и вернуть что-то адекватное клиентам.

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

6. Сохранение/получение данных

Часто в целях экономии времени в контроллерах появляется код, получающий или сохраняющий объекты, используя Репозитории. Если контроллер предоставляет только CRUD операции, то к чёрту, пускай.

У меня даже есть статья, показывающая использование контроллеров таким образом.

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

Для начала взглянем на это с архитектурной точки зрения (фокусируясь на Принципе Единой Ответственности). Если ваши контроллеры используют объекты, предназначенные для хранения данных, то у контроллеров есть явно больше одной причины для изменения.

public IActionResult CheckOutBook(BookRequest request){    var book = _bookRepository.GetBookByTitleAndAuthor(request.Title, request.Author);    // Если у вас уже есть логика получения книги, то вы скорее    // всего захотите добавить сюда и логику выдачи этой книги  // ...return Ok(book);}

За пределами базовых CRUD операций, логика работы с репозиториями является плохим запахом, сигнализирующем о коде, который было бы лучше спрятать поглубже в цепочке обработки запроса.

Именно здесь мне нравится использовать некий сервис (спрятанный за интерфейсом) для обработки запроса или делегирования какому-нибудь CQRS объекту.

На этом всё!


Статья закончилась, а у вас есть ещё примеры, которые не были освещены? Не согласны с каким-то из пунктов? Или просто хотите задать вопрос? Добро пожаловать в комментарии!

Перевод статьи подготовлен в преддверии старта курса "C# ASP.NET Core разработчик".

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

Ссылка для записи на вебинар

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru