C# 15 позволяет объявить тип, который может быть только одним из заранее заданного набора других типов.
Union types. Новое ключевое слово, одна строка, базовый класс не нужен.
Теперь
✔️ Неявное преобразование каждого из допустимых типов напрямую в union
✔️ Компилятор заставляет
✔️ Не нужна иерархия наследования, класс-обёртка или ручные проверки в духе «это Cat или Dog»
Пока это предварительная функция в .NET 11, поэтому синтаксис ещё может измениться до релиза.
Но попробовать уже сейчас вполне можно, если хотите заранее разобраться с новой возможностью.
👉 @KodBlog
Union types. Новое ключевое слово, одна строка, базовый класс не нужен.
public record class Cat(string Name);
public record class Dog(string Name);
public record class Bird(string Name);
public union Pet(Cat, Dog, Bird);
Теперь
Pet может содержать Cat, Dog или Bird — и ничего больше.Pet pet = new Dog("Rex");
string name = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
};✔️ Неявное преобразование каждого из допустимых типов напрямую в union
✔️ Компилятор заставляет
switch обработать все варианты — пропустите один, и проект не соберётся✔️ Не нужна иерархия наследования, класс-обёртка или ручные проверки в духе «это Cat или Dog»
Пока это предварительная функция в .NET 11, поэтому синтаксис ещё может измениться до релиза.
Но попробовать уже сейчас вполне можно, если хотите заранее разобраться с новой возможностью.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤🔥12🤯7❤4🎉3🍾1
Пробовали IExceptionHandler в .NET 10?
Это более простой способ глобально обрабатывать исключения. IExceptionHandler встроен в стандартный middleware обработки ошибок.
Можно обрабатывать либо все исключения, либо только конкретные типы. А значит, обработчики можно выстраивать в цепочку.
👉 @KodBlog
Это более простой способ глобально обрабатывать исключения. IExceptionHandler встроен в стандартный middleware обработки ошибок.
Можно обрабатывать либо все исключения, либо только конкретные типы. А значит, обработчики можно выстраивать в цепочку.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍5🍾1
Please open Telegram to view this post
VIEW IN TELEGRAM
⚡6👍6🥴1🍾1
Если достаточно долго работать с базами данных, рано или поздно столкнёшься с проблемами подключений или параллелизма.
В такой ситуации не стоит просто повышать лимиты в конфигурации. Лучше разобраться в архитектуре, чтобы понимать, почему это происходит и как исправить проблему оптимальным способом.
Эта статья от гениального человека — отличный разбор управления подключениями в Postgres и возможных решений.
Легко свести всё к особенностям Postgres с отдельным процессом на каждое соединение, но похожие проблемы могут возникать и в других базах данных, включая MySQL.
Часто ответ кроется в пуле подключений, но и он добавляет свою сложность, которую тоже важно понимать.
Статье уже 8 лет, но для Postgres она остаётся актуальной и в 2026 году.
Ссылка ниже.
https://brandur.org/postgres-connections
👉 @KodBlog
В такой ситуации не стоит просто повышать лимиты в конфигурации. Лучше разобраться в архитектуре, чтобы понимать, почему это происходит и как исправить проблему оптимальным способом.
Эта статья от гениального человека — отличный разбор управления подключениями в Postgres и возможных решений.
Легко свести всё к особенностям Postgres с отдельным процессом на каждое соединение, но похожие проблемы могут возникать и в других базах данных, включая MySQL.
Часто ответ кроется в пуле подключений, но и он добавляет свою сложность, которую тоже важно понимать.
Статье уже 8 лет, но для Postgres она остаётся актуальной и в 2026 году.
Ссылка ниже.
https://brandur.org/postgres-connections
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥3🍾1
Предпочитайте внедрение зависимостей вместо создания зависимостей вручную.
— Через DI можно управлять временем жизни сервисов.
— Проще реализовывать более продвинутые возможности вроде декораторов.
— Код становится легче покрывать юнит-тестами, подставляя мок-реализации.
Ещё 5 советов по рефакторингу C#: https://milanjovanovic.tech/blog/5-awesome-csharp-refactoring-tips
👉 @KodBlog
— Через DI можно управлять временем жизни сервисов.
— Проще реализовывать более продвинутые возможности вроде декораторов.
— Код становится легче покрывать юнит-тестами, подставляя мок-реализации.
Ещё 5 советов по рефакторингу C#: https://milanjovanovic.tech/blog/5-awesome-csharp-refactoring-tips
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🤯4🍾1
C# 12 убрал приличный кусок шаблонного кода, который вы пишете почти в каждом сервисном классе.
Если вы до сих пор для каждой зависимости объявляете поле, параметр конструктора и потом отдельно присваиваете одно другому, то пишете три строки там, где теперь нужна одна.
Возьмём обычный
С первичным конструктором та же версия занимает около шести строк. Вы просто указываете зависимости прямо в объявлении класса:
Поведение то же, шаблонного кода почти нет.
Перед тем как переписывать всё подряд, стоит помнить три вещи.
Больше не нужно повторять каждую зависимость по три раза. Объявляете её один раз — в заголовке класса.
Параметры первичного конструктора доступны во всём классе, а не только внутри конструктора, поэтому к ним можно обращаться из любых методов.
Тестируемость при этом никуда не исчезает. Моки внедряются точно так же, как раньше.
Есть один нюанс, который может сэкономить вам час: параметры первичного конструктора не становятся полями автоматически. Если вам действительно нужно сохранить параметр именно как поле, сделайте это явно. Не додумывайте за компилятор — он сам подскажет.
Попробуйте сегодня открыть один сервисный класс и перевести его на первичный конструктор. Посчитайте строки до и после.
Разница — это тот шаблонный код, который вам больше не придётся писать.
👉 @KodBlog
Если вы до сих пор для каждой зависимости объявляете поле, параметр конструктора и потом отдельно присваиваете одно другому, то пишете три строки там, где теперь нужна одна.
Возьмём обычный
OrderService. Три readonly-поля. Конструктор с тремя параметрами. Ещё три строки внутри конструктора, где каждый параметр присваивается своему полю. Получается 13 строк, и почти все они нужны только для прокидывания зависимостей.С первичным конструктором та же версия занимает около шести строк. Вы просто указываете зависимости прямо в объявлении класса:
OrderService получает IOrderRepo repo, ILogger и IEmailSender email. После этого repo, logger и email можно напрямую использовать в методах.Поведение то же, шаблонного кода почти нет.
Перед тем как переписывать всё подряд, стоит помнить три вещи.
Больше не нужно повторять каждую зависимость по три раза. Объявляете её один раз — в заголовке класса.
Параметры первичного конструктора доступны во всём классе, а не только внутри конструктора, поэтому к ним можно обращаться из любых методов.
Тестируемость при этом никуда не исчезает. Моки внедряются точно так же, как раньше.
Есть один нюанс, который может сэкономить вам час: параметры первичного конструктора не становятся полями автоматически. Если вам действительно нужно сохранить параметр именно как поле, сделайте это явно. Не додумывайте за компилятор — он сам подскажет.
Попробуйте сегодня открыть один сервисный класс и перевести его на первичный конструктор. Посчитайте строки до и после.
Разница — это тот шаблонный код, который вам больше не придётся писать.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11👎5❤2🥴2😈1
FluentValidation: пример проверки совпадения паролей.
https://github.com/karenpayneoregon/vs2026-how-to/tree/master/MatchPasswordsApp
👉 @KodBlog
https://github.com/karenpayneoregon/vs2026-how-to/tree/master/MatchPasswordsApp
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍4🍾2
Не отправляйте несколько SQL-команд в базу данных последовательно.
Есть более производительный способ.
Когда нужно добавить новую запись, а затем сразу её прочитать, обычно выполняют два SQL-запроса:
С точки зрения выполнения всё нормально, но есть дополнительные расходы:
задержка сети и лишние обращения к базе данных.
На производительности это вполне может сказаться.
С Npgsql этого можно избежать. В нём есть пакетное выполнение запросов.
Суть в том, что несколько SQL-команд отправляются в базу данных за одно обращение вместо отдельного вызова
В Npgsql для этого используется
Важная деталь.
Если вы сами не начали транзакцию, пакет автоматически выполняется внутри неявной транзакции.
Если одна из команд завершается с ошибкой, оставшиеся команды не выполняются, а весь пакет откатывается.
👉 @KodBlog
Есть более производительный способ.
Когда нужно добавить новую запись, а затем сразу её прочитать, обычно выполняют два SQL-запроса:
INSERT и SELECT.С точки зрения выполнения всё нормально, но есть дополнительные расходы:
задержка сети и лишние обращения к базе данных.
На производительности это вполне может сказаться.
С Npgsql этого можно избежать. В нём есть пакетное выполнение запросов.
Суть в том, что несколько SQL-команд отправляются в базу данных за одно обращение вместо отдельного вызова
Execute для каждой команды.В Npgsql для этого используется
NpgsqlBatch: несколько NpgsqlBatchCommand объединяются и отправляются серверу одним запросом.Важная деталь.
Если вы сами не начали транзакцию, пакет автоматически выполняется внутри неявной транзакции.
Если одна из команд завершается с ошибкой, оставшиеся команды не выполняются, а весь пакет откатывается.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3🍾2❤1
Хотите использовать Minimal APIs вместе с архитектурой Vertical Slice?
Вот довольно простая абстракция:
- интерфейс
- несколько методов расширения для их автоматической регистрации;
- целую функциональность можно оформить как отдельный Minimal API endpoint.
Дополнительные затраты при запуске приложения — всего несколько миллисекунд. Практически незаметно.
👉 @KodBlog
Вот довольно простая абстракция:
- интерфейс
IEndpoint для описания конечных точек;- несколько методов расширения для их автоматической регистрации;
- целую функциональность можно оформить как отдельный Minimal API endpoint.
Дополнительные затраты при запуске приложения — всего несколько миллисекунд. Практически незаметно.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🍾1
Please open Telegram to view this post
VIEW IN TELEGRAM
Medium
How Fast is .NET 11 Runtime Async?
Traditional async/await
👍2🍾1
Автор статьи разбирает лучшие практики безопасности REST API в ASP.NET Core.
За годы работы он выпустил и проверил множество .NET API и собрал 18 практик, которые, по его опыту, постоянно помогают держать такие системы безопасными.
В статье есть примеры кода и разбор HTTPS, JWT, политик авторизации, принципа минимальных привилегий, валидации входных данных, защиты от over-posting, ограничения размера запросов, параметризованных запросов и EF Core, rate limiting, CORS, CSRF, хранения секретов, логирования и других базовых мер.
Антон также предлагает сохранить статью как
По его словам, количество найденных проблем может удивить.
👉 @KodBlog
За годы работы он выпустил и проверил множество .NET API и собрал 18 практик, которые, по его опыту, постоянно помогают держать такие системы безопасными.
В статье есть примеры кода и разбор HTTPS, JWT, политик авторизации, принципа минимальных привилегий, валидации входных данных, защиты от over-posting, ограничения размера запросов, параметризованных запросов и EF Core, rate limiting, CORS, CSRF, хранения секретов, логирования и других базовых мер.
Антон также предлагает сохранить статью как
dotnet-security-review-skill для ИИ-агентов и прогнать по ней существующую кодовую базу.По его словам, количество найденных проблем может удивить.
Please open Telegram to view this post
VIEW IN TELEGRAM
Anton Dev Tips
REST API Security Best Practices in ASP.NET Core
18 REST API security best practices in ASP.NET Core - HTTPS, JWT validation, authorization policies, rate limiting, CORS, secrets, security headers, and more - each with C# code you can apply today.
❤7🍾1
Летняя уборка в базе данных. Теперь пора посмотреть на сами запросы.
Включаем
Если вы используете Crunchy Bridge, расширение уже доступно по умолчанию и можно сразу переходить к
При самостоятельном размещении нужно изменить
Можно указать
Затем включаем расширение для нужной базы
Ищем дорогие запросы
Для такой проверки обычно полезнее смотреть на суммарное время выполнения. Запрос средней тяжести, который вызывается миллионы раз, часто наносит больше вреда, чем один редкий медленный запрос.
Также стоит смотреть на среднее время и количество строк. Например, последовательное сканирование может проявиться как большое
Что означают столбцы
•
•
•
•
Статистика копится до ручного сброса
Важно помнить, что сброс удаляет всю накопленную историю. Для анализа можно либо сбросить статистику перед заранее известным периодом высокой нагрузки, либо сравнивать текущие данные с сохранённым ранее снимком.
Что делать с найденными запросами
1. Запустить
2. Проверить недостающие индексы, сбросы на диск из-за
3. После деплоя или добавления индекса сбросить статистику, дать системе поработать под реальной нагрузкой и проверить, уменьшилось ли суммарное время выполнения.
👉 @KodBlog
pg_stat_statements отслеживает форму запросов, количество вызовов и время выполнения по всей базе. Это самый короткий путь от «база почему-то тормозит» до «вот эти пять запросов съедают большую часть времени».Включаем
pg_stat_statementsЕсли вы используете Crunchy Bridge, расширение уже доступно по умолчанию и можно сразу переходить к
CREATE EXTENSION.При самостоятельном размещении нужно изменить
postgresql.conf и перезапустить PostgreSQLshared_preload_libraries = 'pg_stat_statements, ...'
pg_stat_statements.track = top
Можно указать
top или all. Режим all также учитывает запросы внутри функций и процедур. top используется по умолчанию и отслеживает только запросы, выполняемые клиентами.Затем включаем расширение для нужной базы
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
Ищем дорогие запросы
Для такой проверки обычно полезнее смотреть на суммарное время выполнения. Запрос средней тяжести, который вызывается миллионы раз, часто наносит больше вреда, чем один редкий медленный запрос.
SELECT
round(total_exec_time::numeric, 1) AS total_ms,
calls,
round(mean_exec_time::numeric, 2) AS mean_ms,
rows,
query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;
Также стоит смотреть на среднее время и количество строк. Например, последовательное сканирование может проявиться как большое
total_exec_time и большое значение rows у запроса, который в норме должен находить одну конкретную запись.Что означают столбцы
•
calls — сколько раз запрос выполнялся с момента последнего сброса статистики•
total_exec_time и mean_exec_time — суммарное и среднее время выполнения в миллисекундах•
rows — общее количество полученных или изменённых строк•
query — нормализованный запрос с параметрами вида $1Статистика копится до ручного сброса
SELECT pg_stat_statements_reset();
Важно помнить, что сброс удаляет всю накопленную историю. Для анализа можно либо сбросить статистику перед заранее известным периодом высокой нагрузки, либо сравнивать текущие данные с сохранённым ранее снимком.
Что делать с найденными запросами
1. Запустить
EXPLAIN с реальными параметрами. pg_stat_statements показывает форму запроса, но не план выполнения.2. Проверить недостающие индексы, сбросы на диск из-за
work_mem и слишком большое количество мелких запросов приложения вроде N+1.3. После деплоя или добавления индекса сбросить статистику, дать системе поработать под реальной нагрузкой и проверить, уменьшилось ли суммарное время выполнения.
Please open Telegram to view this post
VIEW IN TELEGRAM
🍾1
Микросервисы сильно изменили то, как мы создаём и масштабируем приложения.
Но микросервисные системы очень динамичны. Сервисы могут появляться и исчезать, масштабироваться вверх и вниз и даже перемещаться внутри инфраструктуры.
И здесь возникает проблема. Как сервисам находить друг друга и надёжно взаимодействовать?
Жёстко прописывать IP-адреса и порты — плохая идея. Если экземпляр сервиса сменит адрес или появится новый, система может просто перестать работать.
Для этого и нужен Service Discovery.
Он работает как центральный каталог микросервисов. Сервисы регистрируют в нём своё местоположение, а другие сервисы могут через него находить нужные экземпляры и подключаться к ним.
👉 @KodBlog
Но микросервисные системы очень динамичны. Сервисы могут появляться и исчезать, масштабироваться вверх и вниз и даже перемещаться внутри инфраструктуры.
И здесь возникает проблема. Как сервисам находить друг друга и надёжно взаимодействовать?
Жёстко прописывать IP-адреса и порты — плохая идея. Если экземпляр сервиса сменит адрес или появится новый, система может просто перестать работать.
Для этого и нужен Service Discovery.
Он работает как центральный каталог микросервисов. Сервисы регистрируют в нём своё местоположение, а другие сервисы могут через него находить нужные экземпляры и подключаться к ним.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3🔥2🍾1