codaza
5.33K subscribers
41 photos
27 links
Канал о разработке на платформе .NET с использованием языка программирования C#. Рассматриваются актуальные подходы и современные методологии разработки.

YouTube:
https://www.youtube.com/c/codaza-channel

Контакты:
codaza.channel@gmail.com
Download Telegram
Всем привет!

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

Уверен, что в самых разных источниках, вы читали, что в качестве параметра метода следует использовать IEnumerable<T>, если не требуется функциональность IList<T> или List<T>. Кроме того, хорошей практикой всегда считается следование принципу: Принимай наиболее общее (generic), а возвращай наиболее частное (specific). Это важные принципы, которых стоит придерживаться в подавляющем большинстве случаев. Всё же, бывают ситуации, в которых от этих принципов приходится отступать.

Как вы думаете, есть ли разница в определении параметра метода как IEnumerable<T> или List<T>?

Короткий ответ: да, разница есть.

Дело в том, что, когда параметр метода определяется через интерфейс IEnumerable<T>, в списке выполняются операции boxing и unboxing, что неизбежно ведет к лишним аллокациям в оперативной памяти. Как следствие, мы получаем потери в производительности. Иногда не критичные, а иногда вполне ощутимые. Таким образом, поменяв только тип параметра, мы можем существенно поднять производительность без внесения значительных правок. В моём случае, поставленные цели были достигнуты, и задача решена.

+1 к производительному коду

Всем хороших выходных!
#капитану_на_заметку

Всем привет!

Как здорово генерировать исключения в секции try и ловко ловить их в секции catch 🌈 Одно удовольствие работать с синхронным кодом. Мрачнее дела обстоят при работе с асинхронным кодом. Это как классическая механика и квантовая физика: в асинхронном коде постоянно возникает "квантовая запутанность" и работает там всё по другим законам 🤯

Взгляните на два примера на картинке. В первом варианте мы не ожидаем (await) результата выполнения Task.FromException(), а сразу возвращаем Task, что приводит к несрабатыванию блока catch. Во втором примере мы решаем эту проблему ожидая результат выполнения Task.FromException().

Такая ситуация может приводить к аварийному завершению всего приложения и отыскать причины будет очень непросто. Будьте внимательны!

Поиграться с примером можно по ссылке: https://clck.ru/hcRSc

+1 к тонкостям работы с асинхронным кодом

Всем хороших выходных!
#капитану_на_заметку

Всем привет!

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

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

Посмотрите на пример выше. Вам хочется установить ширину прямоугольника? Сделайте это через свойство Width. Вам хочется установить длину прямоугольника? Да просто сделайте это уже в конце концов! Не пытайтесь препятствовать собственным желаниям 😉

Рефакторингов много, но начните применять этот принцип сегодня, и вы увидите, как радуга падает на ваш исходник 🌈

+1 к простым мудростям

Всем хороших выходных!
Всем привет! 👻

Недавно на канале вышло короткое видео, где я рассказал о 10 лучших практиках, которые стоит применять при разработке приложений на ASP.NET. Одна из этих практик - Глобальная Обработка Исключений (Global Exceptions Handling). В сегодняшнем ролике я раскрываю эту тему более подробно. Наливайте чайку и присаживайтесь поудобнее. Воскресенье — отличный день, чтобы обрабатывать исключения глобально.

Приятного просмотра! 💙

https://youtu.be/RAcpAc0EYV0
#капитану_на_заметку

Всем привет!

Недавно мне довелось провести в аэропорту 9 часов из-за непредвиденной задержки рейса. Было весело 😐 Но примечательно другое - когда мне пришла SMS с напоминаем о вылете, я обратил вниманием что время было указано неправильно, оно выглядело так, как будто смещение GMT было сделано дважды. Не уверен что так должно быть 🤔, но я подумал, что это отличная идея чтобы рассказать подписчикам codaza о необходимости использования перечисляемого типа DateTimeKind.

При определении DateTime, по умолчанию используется DateTimeKind.Unspecified, из-за чего приложение не может понять, какой часовой пояс вы используете. Поэтому очень важно указать формат, с которым вы работаете, иначе это может привести к SMS, которую я получил.

В своих проектах я предпочитаю формат UTC. Таким образом, вся работа с датами идёт в базовом формате, и конвертация в локальный формат производится только при отдаче данных на уровень представления (сайт, SMS и пр.).

+1 к тонкостям работы со временем
#капитану_на_заметку

Всем привет!

На технических собеседованиях часто можно услышать вопрос:
"Как улучшить производительность при конкатенации строк?"
И получить такой ответ:
"Использовать класс StringBuilder! Везде! Всегда!"

Ох... нет... Так это не работает. Но звучит обнадеживающе 🙂

Именно из-за этого ответа, на code review, мне нередко приходится видеть неразумное использование класса StringBuilder. Ненужно использовать StringBuilder на небольших наборах данных. Если у вас пapa-тpoйкa строк, воспользуйтесь обычном оператором "+". Мало того что это гораздо читабельнее, так еще и производительнее. Это ли не то, чего мы так хотим при создании качественного программного обеспечения? 😉

StringBuilder нужен только когда у вас ну ооочень много строк (не три) — вот тогда есть смысл его использования. Неразумное использование StringBuilder может даже приводить к деградации производительности. Именно поэтому нужно всегда отслеживать производительность вашего ПО делая benchmarking.

+1 к работе со строками
#капитану_на_заметку

Всем привет!

А вы знали, что Enum можно прокачать? Для этого нужно добавить атрибут [Flags] там, где мы делаем объявление перечисляемого типа. Это сделает возможным хранение не одного, а множества значений в одной переменной 🪄

Зачем это нужно? Очень нужно 🔥 Вот несколько примеров:

- Файл или запись в базе данных может быть с правами доступа (только чтение, чтение + запись)
- Персонаж в игре может двигаться определённым образом (только вправо, вправо + вверх)
- Продукт может сочетать несколько характеристик (солёный, сладкий + острый)

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

+1 к тонкостям работы с перечисляемыми типами
#капитану_на_заметку

Всем привет!

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

Один из методов, который позволяет упростить сложные синтаксические конструкции в C# называется Pattern matching. Pattern matching предоставляет более краткий синтаксис для тестирования выражений и выполнения действий при их совпадении. Таким образом, вы сможете визуально проще проверять типы и свойства ваших переменных.

Pattern matching — это синтаксический сахар, который помогает сделать ваш код слаще 🧁

+1 к сладости вашего кода
#капитану_на_заметку

Всем привет!

Вам когда-нибудь приходилось делать поиск по строковому ключу в Dictionary? Главная проблема со строковыми ключами — непредсказуемость регистра (Premium, PREMIUM, PrEmIuM).

Можно придумать разные "велосипеды", которые помогут обойти эту проблему (например, везде делать String.ToLower()). Но зачем что-то делать, если можно этого не делать?

Существует классный способ решения вопроса: передать параметр StringComparer.CurrentCultureIgnoreCase в конструкторе Dictionary.

Вот и всё. Вы великолепны! 🍸

+1 к простым решениям
Всем привет! 👻

Сегодня я хочу рассказать вам о том, как управлять конфигурационными данными приложения ASP.NET в development среде. Хранение секретных данных (логины, пароли, токены и пр.) никогда не должны попасть в общедоступный репозиторий. В ASP.NET существует удобный механизм, который решает эту задачу просто и понятно — User Secrets. Поэтому наливайте чайку и давайте посекретничаем 🫖🍪

Приятного просмотра! 💙

https://youtu.be/6OjVlXSKYcg
#капитану_на_заметку

Всем привет!

Бывает так, что нужно прекратить ожидание ранее запущенной задачи (Task) через некоторый промежуток времени, так как её выполнение уже никого не интересует. Разумеется, этот вопрос можно решить через CancellationToken, но .NET6 подарил нам новый API, который поможет сделать это проще.

Класс Task предоставляет метод WaitAsync(), где мы можем передать время, через которое выполнение задачи нас не интересует. Через заданный промежуток времени, ожидание результата выполнения задачи будет прекращено, а также будет сгенерировано исключение TimeoutException.

Подробнее с Task.WaitAsync можно познакомиться в официальной документации.

Вот и всё. Вы великолепны! 🍸

+1 к простым решениям
#капитану_на_заметку

Всем привет!

Entity Framework Core — суперпопулярное решение для работы с базами данных в ASP.NET. При исполнении запросов к базе данных, нередко появляются самые разные ошибки ("Timeout expired", "Transaction was deadlocked" и пр.). Обычным решением в борьбе с такими ошибками, является применение политики повторов. Проще говоря, делается повторная попытка выполнения SQL-запроса через некоторый Timeout.

Для решения этой несложной задачи, начинающими (и не очень 😉) разработчиками, придумывается огромное количество "велосипедов". Однако существует стандартное решение — EnableRetryOnFailure. Данный метод позволяет настроить контекст на использование стратегии повторов. Это делается очень просто в файле Program.cs.

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

Подробнее можно прочитать в официальной документации.

-1 велосипед, который так и тянет написать
#капитану_на_заметку

Всем привет!

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

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

configuration.GetValue<string>("MyConfig:Parameter1");

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

Такой подход существенно упрощает работу с вложенными структурами в конфигурации вашего приложения.

Вот и всё. Вы великолепны! 🍸

+1 к простым решениям
#капитану_на_заметку

Всем привет!

Большинство web-сервисов предоставляют возможности для интеграции через REST API. За примерами далеко ходить не надо: Google, GitHub, YouTube, Facebook и много-много других — все предоставляют возможность взаимодействия через некоторое API.

Читая документацию на API, вы можете столкнуться с требованием к частоте запросов. Например, максимальная частота запросов не должна превышать 55 в секунду с одного IP-адреса.

Главный вопрос для нас: как добиться выполнения этих требований быстро и просто? Желательно еще, чтобы без смс и регистрации 😉

Существует очень классная библиотека — RateLimiter. Вам просто нужно завести некоторую переменную-ограничитель с указанием той периодичности, которая требуется и дальше пользоваться ей везде перед запросом. Причем вы можете организовывать составные ограничители (например, не более 10 запросов в секунду + не более 1000 запросов в сутки).

Познакомиться подробнее с RateLimiter можно тут.

+1 в сундучок с инструментами юного разработчика
#капитану_на_заметку

Всем привет!

Комментирование исходного кода — это здорово! Открыл исходник и вот он комментарий. Но как прокомментировать SQL-запрос, отправляемый к базе данных, когда мы используем Entity Framework?

Запросы к базе данных могут анализироваться не только разработчиком приложения, но и администратором базы данных. Хотелось бы показать всем разработчикам цель того или иного SQL-запроса без длительного анализа.

Entity Framework предлагает отличный метод TagWith(), который поможет добавить комментарий к SQL-запросу. Комментарий поможет раскрыть намерение запроса и поможет при анализе не только вам, но и разработчикам баз данных.

Вот и всё. Вы и ваша команда великолепны! 🍸

+1 к искусству качественного логирования
#капитану_на_заметку

Всем привет!

Нельзя сказать, что генерация исключений в C# выглядит изящно. Чаще всего это громоздкие конструкции, которые выражают несложную логику, но делают это максимально неуклюже. Естественно, это мешает спокойному восприятию кода в целом.

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

Существует классная библиотека, которая называется Throw. С её помощью, генерация исключений выполняется максимально просто и интуитивно понятно. Пример на картинке — это лишь малая часть возможностей. Например, там есть такие классные штуки как:

name.Throw().IfEmpty();
name.Throw().IfNotContains("substring");

и многое другое.

Ловите ссылку на GitHub Throw тут.

+1 к чистой работе с исключениями
#капитану_на_заметку

Всем привет!

Мне нравится использовать подход Code First. Особенно удобно использовать этот подход при реализации небольших микросервисов с несложной бизнес-логикой.

Одна из проблем, которую мне нередко приходится наблюдать при проведении code review — отсутствие ограничений на строковые значения. Если не указать ограничение строки явно, то все string будут интерпретированы Entity Framework Core в SQL как NVARCHAR(MAX).

Использование NVARCHAR(MAX) везде и всюду плохо по многим причинам, главная из которых — снижение производительности базы данных.

Как это исправить быстро, без смс и регистрации? Просто укажите над свойством атрибут [MaxLength(n)], где n - длина строки, которая вас устраивает.

Вот и всё. Теперь ваши сервисы будут работать быстрее, выше, сильнее.

+1 к грамотному использованию Code First
#капитану_на_заметку

Всем привет!

Рефакторить код и визуально уменьшать синтаксические конструкции C# с помощью ReSharper может быть весело. Я очень люблю это делать!

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

Одним из таких рефакторингов является превращение lambda expression 👉 method group. Действительно, method group выглядит гораздо привлекательнее, но lambda expression может кэшироваться средой выполнения .NET, что избавляет от лишних выделений в оперативной памяти.

Не то чтобы нужно срочно бежать и менять всё на лямбды, но бдительность терять не стоит 👀

+1 к подозрительности при рефакторинге
#капитану_на_заметку

Всем привет! 👻

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

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

Просто скажите, что собираетесь использовать коллекцию только для чтения IReadOnlyList<T>. Теперь любые попытки изменения коллекции станут невозможными еще на этапе компиляции, а ваше намерение чётким и ясным.

+1 к коду, за который тебя не будут искать

Счастливого Хэллоуина! 🎃