C# 1001 notes
6.54K subscribers
397 photos
12 videos
2 files
344 links
Регулярные короткие заметки по C# и .NET.

Просто о сложном для каждого.

admin - @haarrp
Download Telegram
Крутая статья - «Building an Event Queue in ASP.NET Core» от Deepumi.

🔹 Она показывает, как правильно построить очередь событий внутри ASP.NET Core-приложения:
• использовать встроенные механизмы (middleware / DI)
• распределять события между обработчиками
• обрабатывать события асинхронно и надёжно

🔹 Такая архитектура помогает:
• реализовать decoupled компоненты, которые не знают друг о друге
• централизовать событие-поток (логика, оповещения, триггеры и т.п.)
• легче масштабировать и тестировать код

Если работаешь с ASP.NET Core и хочешь сделать систему событий - стоит заглянуть.

Подробнее: deepumi.com/blog/building-an-event-queue-in-aspnet-core.html
🚦 Feature Flags в .NET - как управлять релизами без redeploy

Feature flags (фиче-флаги) позволяют включать и выключать функциональность на лету, без повторного деплоя и риска для продакшена.

Идея простая:
код задеплоен → поведение управляется конфигурацией.

Что это даёт на практике:
— Постепенные релизы
Можно включить новую фичу сначала для 1%, 10% или конкретной группы пользователей.
— Быстрый rollback
Если что-то пошло не так — просто выключаете флаг. Без откатов и срочных хотфиксов.
— A/B тесты
Разные пользователи получают разное поведение одного и того же кода.
— Targeting пользователей

Фичи можно включать:
• по user id
• по роли
• по региону
• по environment (dev / staging / prod)

— Меньше фиче-веток
Код живёт в main, а не за флагами в git.

В .NET обычно используют:
- Microsoft.FeatureManagement
- Azure App Configuration
- LaunchDarkly / Unleash / ConfigCat

Где это особенно полезно:
- публичные API
- high-traffic сервисы
- SaaS-продукты
- экспериментальные и рискованные фичи

Коротко:
Feature flags превращают релиз из «одного опасного момента» в управляемый процесс.

Это один из самых мощных инструментов для зрелой backend-архитектуры.

👉 Подробнее
🛠 Как оживить протухшую ветку без merge-хаоса

Бывает: вы увлеклись разработкой, прошло пару недель (или месяцев), а основная ветка уже ушла далеко вперёд. В итоге — боль, конфликты и бесконечные merge-коммиты.

В таких случаях может спасати ребейз на свежую ветку:

git pull --rebase origin release/1.2.0


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

Конфликты всё равно придётся разруливать, но по одному — в контексте конкретного коммита, а не в гигантской свалке.

После успешного ребейза пушим с --force-with-lease, чтобы аккуратно обновить удалённую ветку, и продолжаем работать так, как будто отставания и не было.
🔥 Как правильно работать с конфигурацией в .NET

Самый чистый и масштабируемый способ получать настройки в .NET - это Options pattern.

Где живет конфигурация
Настройки приложения могут приходить из разных источников:
- переменные окружения
- JSON-файлы appsettings
- user secrets
- другие configuration providers

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

Поэтому IConfiguration напрямую лучше не использовать в бизнес-коде.

Options pattern - как правильно
Вместо этого используется Options pattern:

1. Создаешь класс настроек
- один класс = одна логическая группа конфигурации

2. Биндишь его к appsettings.json
- через services.Configure<T>

3. Используешь настройки через DI
- IOptions
- IOptionsSnapshot
- IOptionsMonitor

Плюсы подхода
- строгая типизация
- автокомплит в IDE
- централизованная конфигурация
- проще рефакторить
- можно добавить валидацию через data annotations

Например:
- [Required]
- [Range]
- [EmailAddress]

Это позволяет ловить ошибки конфигурации при старте приложения, а не в продакшене.

Важно знать
Существуют разные интерфейсы:
- IOptions - статические настройки на все время жизни приложения
- IOptionsSnapshot - обновляются на каждый запрос (scoped)
- IOptionsMonitor - отслеживают изменения конфигурации в рантайме

Понимание разницы между ними сильно влияет на корректность архитектуры.

Если пишешь production .NET - Options pattern должен быть стандартом по умолчанию.

https://www.milanjovanovic.tech/blog/how-to-use-the-options-pattern-in-asp-net-core-7
Forwarded from C# (C Sharp) programming
3 простые оптимизации, которые реально ускоряют код

1️⃣ Забирай данные пачкой
Меньше запросов — меньше сетевых задержек.
Вместо десятков запросов — один IN (...).

2️⃣ Делай больше параллельно
Если задачи не зависят друг от друга — выполняй их одновременно.
Асинхронность часто даёт бесплатный прирост скорости.

3️⃣ Кэшируй результаты
Если данные не меняются — не пересчитывай и не запрашивай их заново.
Память дешевле времени.

Никакой магии и сложных алгоритмов — просто базовые приёмы, которые в реальных проектах дают самый заметный эффект.
Forwarded from C# (C Sharp) programming
✔️ C# стал языком 2025 года по версии TIOBE.

Индекс TIOBE подвел итоги года: звание «Язык 2025 года» досталось C#, который показал рекордный рост популярности (+2.94%)? однако в общем зачете он по-прежнему занимает 5-ю строчку. Абсолютным лидером остается Python с 22.61% долей рынка.

В первой пятерке произошли перестановки: язык C поднялся на 2 место, сместив C++ на 4-ю позицию; 3 место досталось Java, а R вернулся в топ-10. Провал года - Go, который неожиданно сдал позиции, опустившись сразу на 16-е место.

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

https://www.tiobe.com/tiobe-index/
Please open Telegram to view this post
VIEW IN TELEGRAM
⚡️ Автоматическая регистрация Minimal APIs в .NET - без ручного маппинга

Если в проекте 20+ endpoint’ов, app.MapGet/MapPost превращается в ад.
Решение - авторегистрировать endpoints через DI.

Идея:
1) Делаешь общий интерфейс IEndpoint
2) Каждый endpoint реализует его
3) На старте приложения сканируешь сборку, регистрируешь все реализации в DI
4) Достаёшь их из DI и вызываешь MapEndpoints()

Плюсы:
чистый Program.cs
каждый endpoint в отдельном файле
масштабируется без хаоса
легко тестировать и поддерживать

Пример паттерна:


builder.Services.AddEndpoints(typeof(Program).Assembly);

public interface IEndpoint
{
void Map(IEndpointRouteBuilder app);
}

public static class EndpointExtensions
{
public static IServiceCollection AddEndpoints(this IServiceCollection services, Assembly assembly)
{
var endpoints = assembly.DefinedTypes
.Where(t => !t.IsAbstract && !t.IsInterface && typeof(IEndpoint).IsAssignableFrom(t))
.Select(t => ServiceDescriptor.Transient(typeof(IEndpoint), t))
.ToArray();

services.TryAddEnumerable(endpoints);
return services;
}

public static void MapEndpoints(this WebApplication app)
{
foreach (var endpoint in app.Services.GetServices<IEndpoint>())
endpoint.Map(app);
}
}
🤖 Open Claude Cowork: AI-партнёр для программирования

Open Claude Cowork — это настольный AI-ассистент, который помогает в программировании, управлении файлами и выполнении задач. Он совместим с Claude Code и предлагает визуальный интерфейс для удобной работы с AI, позволяя легко управлять сессиями и получать результаты в реальном времени.

🚀Основные моменты:
- 🖥️ Настольное приложение с визуальным интерфейсом
- 🤖 AI-партнёр для выполнения задач
- 🔁 Полная совместимость с Claude Code
- 📂 Удобное управление сессиями и историей
- 🔐 Контроль разрешений для безопасных действий

📌 GitHub: https://github.com/DevAgentForge/Claude-Cowork
API Input Validation в .NET: почему FluentValidation лучше, чем Data Annotations

Data Annotations отлично подходят для простых правил:

[Required] - ок
[MaxLength(50)] - норм

Но как только тебе нужно что-то “умнее”, начинается боль:

- проверить данные в базе
- валидировать по настройкам из appsettings.json
- вызвать сервис и принять решение динамически

Data Annotations упираются в потолок, потому что Attribute - это статичная штука.
Туда не получится нормально прокинуть зависимости через DI.

И вот здесь FluentValidation реально сияет

Почему:
FluentValidation-валидаторы - это обычные классы, которые регистрируются в DI контейнере.
А значит, внутрь можно инжектить что угодно:

- сервисы
- конфиги
- репозитории
- кэш
- внешние API

Пример:
нужно проверить, что CouponCode валиден через IPricingService?
Просто инжектишь IPricingService в конструктор валидатора и делаешь проверку.

В итоге валидация превращается из “статической проверки полей”
в полноценный слой логики - динамический, умный и расширяемый.

FluentValidation = правильная валидация для реального продакшена.
✔️ C# может прокачать collection expressions: “аргументы при создании коллекции”

Есть прикольное предложение в csharplang: сделать так, чтобы в collection expressions (`[a, b, c]`) можно было передавать аргументы в создание коллекции.

Проблема сейчас:
Postgres-стайл удобный синтаксис уже есть:

List<int> xs = [1, 2, 3];

Но если тебе важно задать, например, capacity (чтобы не было лишних realloc внутри списка), то приходится писать “старым способом”:

var xs = new List<int>(capacity: 32) { 1, 2, 3 };

Предлагаемое решение:
добавить возможность передавать аргументы прямо в collection expression:

List<int> xs = [args(capacity: 32); 1, 2, 3];

То есть:
- args(...) - это аргументы для конструктора / create-метода
- после ; - элементы коллекции

Зачем это нужно:
- можно сохранить суперкороткий синтаксис [ ... ]
- но при этом контролировать создание коллекции (capacity, comparer и т.д.)
- меньше лишних аллокаций → быстрее в hot-path коде

Если фича дойдёт до релиза, это будет реально удобный апгрейд синтаксиса коллекций в C#.

https://github.com/dotnet/csharplang/blob/main/proposals/collection-expression-arguments.md
Please open Telegram to view this post
VIEW IN TELEGRAM
🖥 Если ты делаешь бэкенд на .NET и хочешь расти не «по туториалам», а в сторону прод-уровня, архитектуры и реальных систем - это золото.

Архитектура и структура проектов
N-Layered vs Clean vs Vertical Slice Architecture
https://antondevtips.com/blog/n-layered-vs-clean-vs-vertical-slice-architecture

Лучшая структура .NET-проектов с Clean Architecture и Vertical Slices

https://antondevtips.com/blog/the-best-way-to-structure-your-dotnet-projects-with-clean-architecture-and-vertical-slices

Зачем писать архитектурные тесты
https://antondevtips.com/blog/why-do-you-need-to-write-architecture-tests-in-dotnet

От модульного монолита к микросервисам
https://antondevtips.com/blog/migrating-modular-monolith-to-microservices-in-dotnet

API и бэкенд-практики
Best Practices для REST API

https://antondevtips.com/blog/best-practices-for-building-rest-apis

90% API не RESTful - что вы упускаете
https://antondevtips.com/blog/90-of-apis-are-not-restful-what-youre-missing-and-when-it-matters

Как ускорить Web API
https://antondevtips.com/blog/how-to-increase-performance-of-web-apis-in-dotnet

Аутентификация и авторизация в ASP.NET Core
https://antondevtips.com/blog/authentication-and-authorization-best-practices-in-aspnetcore

Интеграционные тесты в ASP.NET Core
https://antondevtips.com/blog/asp-net-core-integration-testing-best-practises

EF Core и данные
Почему не нужен Repository поверх EF Core
https://antondevtips.com/blog/why-you-dont-need-a-repository-in-ef-core

5 скрытых NuGet-пакетов для EF Core
https://antondevtips.com/blog/5-hidden-efcore-nuget-packages

Кеширование в .NET
https://antondevtips.com/blog/how-to-implement-caching-strategies-in-dotnet

Resilience, инфраструктура, прод
Retry и устойчивость с Polly и Microsoft Resilience

https://antondevtips.com/blog/how-to-implement-retries-and-resilience-patterns-with-polly-and-microsoft-resilience

Загрузка больших файлов в Azure Blob Storage
https://antondevtips.com/blog/implementing-large-file-uploads-and-downloads-in-azure-blob-storage-with-dotnet

Деплой в Azure с Neon Postgres и .NET Aspire
https://antondevtips.com/blog/how-to-deploy-dotnet-application-to-azure-using-neon-postgres-and-dotnet-aspire

Job Scheduler TickerQ
https://antondevtips.com/blog/tickerq-the-modern-dotnet-job-scheduler-that-beats-quartz-and-hangfire

Observability
Старт с OpenTelemetry, Jaeger и Seq
https://antondevtips.com/blog/getting-started-with-open-telemetry-in-dotnet-with-jaeger-and-seq

Код и язык
Как писать чище и лучше код в .NET
https://antondevtips.com/blog/how-to-write-better-and-cleaner-code-in-dotnet

Новые фичи .NET 10 и C# 14
https://antondevtips.com/blog/new-features-in-dotnet-10-and-csharp-14

Extension Members в C# 14
https://antondevtips.com/blog/extension-members-in-csharp14-changed-how-we-write-code-forever

Практические проекты
Invoice Builder на .NET с IronPDF
https://antondevtips.com/blog/how-to-build-a-production-ready-invoice-builder-in-dotnet-using-ironpdf

Ошибки разработчиков
Top 10 вещей, которые должен делать .NET-разработчик в 2026

https://antondevtips.com/blog/top-10-things-every-dotnet-developer-needs-to-do-in-2026

15 ошибок .NET-разработчиков
https://antondevtips.com/blog/top-15-mistakes-dotnet-developers-make-how-to-avoid-common-pitfalls

15 ошибок при создании Web API
https://antondevtips.com/blog/top-15-mistakes-developers-make-when-creating-web-apis

Это не «почитать на досуге». Это roadmap от уровня “пишу контроллеры” до “проектирую систему, которая живёт в проде годами”.
Please open Telegram to view this post
VIEW IN TELEGRAM
Есть соглашения по именованию в коде, которые все постоянно нарушают?


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

• договорённостях в голове
• комментариях в Wiki
“ну мы же так решили”

А потом кто-то добавляет UserCmdHandler, DoStuffManager или NewService2 - и структура просто разваливается.

💡 Решение: архитектурные тесты

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

Например:

все CommandHandler обязаны заканчиваться на CommandHandler
• классы из Application не должны ссылаться на Infrastructure
• контроллеры не имеют доступа к репозиториям напрямую

DTO не должны жить в доменной модели

И если кто-то нарушил правило - тест падает. Сразу. В CI.

⚙️ Что это даёт

Архитектура перестаёт быть кривой и косой.
Она начинает подчиняться строгим правилам.

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

🧠 Главное осознание


Архитектурные тесты защищают структуру.

А структура - это то, что определяет, станет проект поддерживаемым…
или превратится в хаос через год.
⚡️ Миграция хеширования паролей без поломки логина

Ты сделал маленький и вроде бы аккуратный рефакторинг - и сломал весь логин.

Логику хеширования паролей ты аккуратно вынес в отдельный слой. Отлично.
А потом решил обновиться — заменить PBKDF2 на bcrypt.

Просто поменял реализацию. Казалось бы, безопасно.

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

Ты только что внёс breaking change, даже не заметив этого.

Это классическая ловушка в аутентификации — менять алгоритм хеширования без стратегии миграции.

Важно понимать:
- пароли в базе уже захешированы старым алгоритмом
- новый алгоритм не умеет их проверять
- простой swap реализации = мгновенный outage
-
⚡️Правильный подход:
- поддерживать несколько алгоритмов одновременно
- определять алгоритм по формату хеша
- при успешном логине пересохранять пароль новым алгоритмом
- без сброса паролей
- без даунтайма

Маленькие изменения в use case-ах, особенно в аутентификации, могут иметь огромные последствия.
This media is not supported in your browser
VIEW IN TELEGRAM
🚀 LOAD BALANCER ЗА 1 МИНУТУ

Load Balancer - это «диспетчер трафика» между пользователями и серверами.

Когда пользователей становится много, один сервер перестаёт справляться:
- 500 пользователей — работает нормально
- 1 000 — начинает тормозить
- 10 000 — может упасть из-за перегрузки

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

Проблемы без Load Balancer:

- Один сервер = одна точка отказа
- Любой сбой или проблема с сетью — приложение полностью недоступно
- Ограниченная мощность
- При росте нагрузки — медленные ответы и падения

Как работает Load Balancer:

1. Все запросы сначала попадают в Load Balancer
2. Он проверяет, какие серверы работают и доступны
3. Распределяет трафик по серверам на основе:
- текущей нагрузки
- времени ответа
- доступности
4. Если сервер перестаёт отвечать — трафик автоматически перенаправляется на рабочие

В результате:
- нагрузка распределяется равномерно
- используются только «здоровые» серверы
- уменьшаются задержки
- система остаётся стабильной

Зачем нужен Load Balancer:

- Scalability — можно добавлять новые серверы без изменений на стороне клиента
- High Availability — если один сервер падает, система продолжает работать
- Better Performance — запросы обрабатываются быстрее
- Efficient resource usage — равномерное использование ресурсов и отсутствие узких мест

Главная идея:

Load Balancer — основа масштабируемых и отказоустойчивых систем. Без него любое приложение рано или поздно упрётся в предел одного сервера.

Подписывайся, больше фишек каждый день !


// Пример конфигурации Nginx как Load Balancer

http {
upstream backend {
server 192.168.1.10;
server 192.168.1.11;
server 192.168.1.12;
}

server {
listen 80;

location / {
proxy_pass http://backend;
}
}
}
⚡️ URL shortener за <100 строк на .NET - реально

Идея простая: у тебя есть входной URL -> генеришь короткий код -> сохраняешь в БД -> по коду делаешь редирект.

Что нужно собрать

- Генератор уникального кода
Делай base62 (0-9, a-z, A-Z) длиной 6-8 символов.
Главное - гарантировать уникальность:
- либо проверка в БД и повтор генерации при коллизии
- либо уникальный индекс по Code и ретрай при ошибке сохранения

- База данных
Таблица ShortenedUrl:
- Id (Guid)
- LongUrl (string)
- Code (string, unique)
- CreatedOnUtc (DateTime)
Опционально:
- ExpiresOnUtc
- Clicks
- CreatedByIp

- 2 эндпоинта (Minimal API)
- POST /shorten
- валидируешь URL (Uri.TryCreate)
- генеришь code
- формируешь shortUrl из scheme + host + code
- сохраняешь в БД
- возвращаешь shortUrl
- GET /{code}
- ищешь code в БД
- если нет - 404
- если есть - Results.Redirect(LongUrl)

Почему чаще всего "падают" такие сервисы

- Коллизии кода -> решается unique index + retry
- Открытый редирект на мусор -> валидируй UriKind.Absolute и при желании режь опасные схемы (только http/https)
- Производительность поиска -> индекс по Code обязателен
- Правильный shortUrl за прокси -> если сервис за nginx/cloudflare, учитывай Forwarded Headers, иначе host/scheme будут неправильными

Если делать максимально чисто - генератор кода отдельным сервисом, модель + DbContext, и два эндпоинта. Это и укладывается в <100 строк.
В .NET 8 появился простой способ сделать HttpClient устойчивым к сбоям — буквально одной строкой.

Microsoft добавила библиотеку Microsoft.Extensions.Http.Resilience, в которой уже есть готовые pipeline’ы для обработки ошибок при HTTP-запросах.

Что это даёт из коробки:
- Retry при временных сбоях
- Timeout
- Circuit Breaker
- Rate limiting
- Защиту от перегрузки

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


services.AddHttpClient<GitHubService>(static httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
})
.AddStandardResilienceHandler();
Нам и так нормально.
This media is not supported in your browser
VIEW IN TELEGRAM
⚡️ Языки программирования и их самые любимые фичи

🐍 Python - чистый и читаемый синтаксис
🖥️ BASIC - очень дружелюбен для новичков
📊 Visual Basic - простое создание GUI
🟨 JavaScript - запускается везде и сразу
🐘 PHP - очень простой деплой веб-приложений
💎 Ruby - красивый и элегантный синтаксис
🎵 Groovy - бесшовная интеграция с Java
Java - огромная экосистема и стабильность
🟣 C# - отличные инструменты и IDE
🐹 Go - простая и быстрая конкурентность
🐦 Swift - современный и безопасный дизайн
• 🅺 Kotlin - встроенная защита от null
🎯 Dart - отлично работает с Flutter
🧮 Fortran - сверхбыстрые научные вычисления
🔧 C - полный контроль над железом
🍎 Objective-C - мощная динамическая runtime-система
🔺 Scala - сочетание функционального и ООП
Zig - простой и предсказуемый системный код
🐪 Perl - невероятно мощная обработка текста
🚀 C++ - высокая производительность и контроль
🦀 Rust - безопасность памяти без garbage collector
⚙️ Assembly - максимальный контроль и производительность
Forwarded from C# (C Sharp) programming
Vector Search - как это работает (и почему это важно для .NET разработчиков)

Vector search ищет смысловую близость, а не просто точные совпадения.

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

То есть система ищет не одинаковые слова, а похожие по смыслу объекты.

Почему это важно?

Vector search лежит в основе многих AI-функций:

- семантический поиск
- рекомендательные системы
- интеграции с LLM
- умные ассистенты внутри приложений

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

Это дает реальную бизнес-ценность - от поиска по документам до персонализированных рекомендаций.

📍 Полный пример реализации
Полезный паттерн для Minimal APIs в .NET, если вы хотите организовать проект по принципу Vertical Slice Architecture.

Идея очень простая.

Вместо того чтобы держать все роуты в Program.cs, каждый endpoint выносится в отдельный класс.

Создаётся небольшой интерфейс:


public interface IEndpoint
{
void MapEndpoint(IEndpointRouteBuilder app);
}


Дальше каждый endpoint просто реализует этот интерфейс:

public class GetFollowerStats : IEndpoint
{
public void MapEndpoint(IEndpointRouteBuilder app)
{
app.MapGet("users/{userId}/followers/stats", async (
Guid userId,
ISender sender) =>
{
var query = new GetFollowerStatsQuery(userId);

Result<FollowerStatsResponse> result = await sender.Send(query);

return result.Match(Results.Ok, CustomResults.Problem);
})
.WithTags(Tags.Users);
}
}


Что это даёт:
• endpoints изолированы по фичам
• код становится намного чище
• проще масштабировать API
• удобно использовать вместе с CQRS / MediatR

Регистрация таких endpoints занимает буквально пару миллисекунд при старте приложения.

Отличный способ держать Minimal API структурированным даже в больших проектах.
Forwarded from C# (C Sharp) programming
🚀 Почему этот EF Core код тормозит?

Технически - всё ок.
По производительности не очень.

Вот типичная ошибка:

Загружаешь всю сущность (все колонки)
Потом фильтруешь и мапишь уже в памяти

Что происходит:
- лишние данные тянутся из БД
- растёт нагрузка на сеть
- увеличивается потребление памяти
- замедляется приложение

Как правильно:

Используй проекцию через `.Select()` прямо в запросе:

- берёшь только нужные поля
- меньше данных из БД
- быстрее запрос
- меньше нагрузка на систему

📌 Правило простое:
Не тащи всё - бери только то, что используешь

Именно такие мелочи чаще всего дают x2–x10 к скорости.