A state of .NET


Kanal geosi va tili: Rossiya, Ruscha


A State of .NET — всё для .NET-разработчика: новости, релизы, статьи о C#, .NET, ASP.NET Core, EF Core, и прочих технологиях.
Ежедневно — новости, релизы, обзоры статей и книг, лучшие практики и инструменты.

Bog‘liq kanallar

Kanal geosi va tili
Rossiya, Ruscha
Statistika
Postlar filtri




Kafka гарантирует порядок? Разбираем 3 инцидента с Outbox

#Kafka #PostgreSQL #Debezium

✅
Один order_id на все события даёт размещение в партиции, но не причинную последовательность: ключ не определяет, в каком порядке события дошли до брокера.

📈
«Порядок» распадается на разные свойства, и за каждое отвечает своё звено — пока это не записано в требовании, упорядоченность остаётся надеждой.

🔑 Ключ даёт размещение, aggregate_version — позицию в истории, бизнес-правило — допустимость. Если сборка закоммитилась раньше оплаты, номер честно зафиксирует порядок, но история будет недопустимой.

⚠️ exactly-once не чинит перестановку событий от разных процессов и не объединяет PostgreSQL с Kafka в одну транзакцию: повтор и перестановка — разные проблемы с разными решениями.

Читаем:
Внутри — разбор трёх инцидентов по дампу партиции (включая случай, где у одного инцидента две независимых причины, и случай, где событие потеряно, а не переставлено), почему `last offset + 1` ломает прогресс чтения, и как переписать НФТ так, чтобы оно пережило смену базы и брокера.

https://habr.com/ru/companies/otus/articles/1077958/

@aStateOfNet




AS4 в .NET без Java-шлюза: ebMS 3.0, WS-Security и неотрекаемые квитанции шагом маршрута

#dotnet #AS4 #ebMS

✅
AS4 защищает сам документ, а не только канал: получатель возвращает подписанную квитанцию, чьи дайджесты криптографически привязаны к тому, что вы подписали — доказательство доставки именно этого документа, байт в байт.

📈
Нативный .NET-коннектор ставит AS4 внутрь маршрута (From → To), убирая отдельный Java-MSH или коммерческий шлюз с их inbox-каталогом, джобой-подборщиком и отдельной командой сопровождения.

🔐 Крипто-слой пришлось писать вручную: SignedXml из .NET не разрешает ссылки cid: на MIME-вложения, а EncryptedXml не умеет ни AES-GCM, ни OAEP с MGF1-SHA256 (поддерживает только OAEP-SHA1) — ровно те «дыры» в API, из-за которых готовой реализации в мейнстриме .NET нет.

🔁 Повтор по AS4 — это те же байты, тот же MessageId и та же подпись: Domibus на повтор отдаёт сохранённую квитанцию первой передачи, и пересборка сообщения с новой подписью сломала бы сверку дайджестов.

Читаем:
В статье — разбор профиля eDelivery AS4 1.16 (что принимается, что отвергается с какими EBMS-кодами), механика идемпотентности и дедупликации при рестарте и за балансировщиком, защита от signature wrapping и replay, потоковая обработка больших вложений с лимитами, результаты интеропа против Holodeck B2B и Domibus, а также честные границы (нет Pull, асинхронных квитанций, AS4 2.0).

https://habr.com/ru/articles/1089204/

@aStateOfNet


Video oldindan ko‘rish uchun mavjud emas
Telegram'da ko‘rish


Поменяли object на Lock, и два потока встретились в критической секции

#dotnet #csharp #concurrency

✅
С C# 13 тип выражения в lock решает семантику, а не только скорость: object разворачивается в Monitor, System.Threading.Lock — в EnterScope, и это два несовместимых замка на одном объекте.

📈
Один объект Lock, прочитанный через object или через generic-параметр, тихо возвращает вас к Monitor — и взаимное исключение исчезает без единой ошибки компиляции.

🔧 Утечка происходит не в самом lock, а в строке, где экземпляр кладут в поле типа object или в Dictionary: два потока по 150 000 проходов встретились в критической секции 106 309 раз, счётчик потерял 4487 инкрементов.

⚠️ Обёртка `lock (gate)` в универсальном методе `LockAny` компилируется в Monitor молча — предупреждения нет, а `where T : Lock` невозможен, потому что Lock объявлен sealed.

Читаем:
В статье разбор того, где именно компилятор молчит, почему Monitor.IsEntered начинает врать, что происходит с Monitor.Wait/Pulse, замеры памяти (Lock +16 байт на экземпляр против раздувания Monitor) и порядок безопасной миграции через CS9216 как ошибку сборки.

https://habr.com/ru/companies/otus/articles/1087292/

@aStateOfNet


OLAP OVER HTTP: как отдавать большие аналитические данные через API и не положить сервис

#ClickHouse #DotNet #HighLoad

✅
OLAP-базу не стоит ставить под каждый пользовательский HTTP-запрос — она захлёбывается на RPS, а не на объёме данных.

📈
Разнеси нагрузку по компонентам: hot storage для частых запросов к свежим данным, S3 для готовых отчётов, ClickHouse для тяжёлой аналитики.

🔹 ClickHouse на стенде (4 ГБ RAM, 6 ядер) держал ~60 RPS при ~80 ms, а на 80 RPS полностью захлёбывался — каждый запрос жрёт максимум ядер, отсюда Thread Contention и Noisy Neighbor.

🔹 Перенос горячих данных (99% запросов) в PostgreSQL дал ~240 RPS и 12 ms против 80 ms — за счёт Index Scan по 0,02% строк на селлера, а кеш отчётов в S3/MinIO вытянул ~750 RPS.

Читаем:
В статье разобраны конкретные подводные камни каждого подхода (пересоздание битого отчёта, 404 против генерации на лету, отсутствие пула соединений для ClickHouse) и расчёт, при каком числе запросов кеширование начинает выигрывать.

https://habr.com/ru/companies/ozontech/articles/1084762/

@aStateOfNet




Как и зачем я сделал свой маппер

#dotnet #csharp #sourcegenerator

✅
Незаполненное поле в цели маппинга должно ломать сборку, а не молча уезжать в прод как default.

📈
Изменение модели ломает билд, а не отдаёт пользователям `0001-01-01T00:00:00` вместо реальной даты.

🔧 `required` не спасает с рантайм-мапперами: AutoMapper, Mapster создают объекты через рефлексию мимо object initializer, поэтому CS9035 не срабатывает. Ловит только Mapperly, генерирующий настоящий object initializer.

⚡ FusionMapper совмещает source generator и рантайм-фоллбек: где типы известны на компиляции — работают C# Interceptors (ошибка сборки при незаполненном required), где типы приходят из generic-кода — маппинг строится в рантайме на первом вызове.

Читаем:
Разбор, почему интерсепторы нельзя применять внутри expression tree и как это ломало компилятор без сообщения об ошибке, плюс бенчмарки против ручного маппинга, Mapperly, Mapster и AutoMapper на плоских и вложенных графах.

https://habr.com/ru/articles/1087338/

@aStateOfNet


Как я перенёс сборку лексера в compile-time

#dotnet #roslyn #sourcegenerators

✅
Один source generator может передать результат другому через post-initialization output — эта фаза добавляет исходники в компиляцию до запуска обычных генераторов, поэтому [GeneratedRegex] видит сгенерированную константу.

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

⚙️ Порядок альтернатив в регулярке — это правило языка: FloatLiteral с приоритетом 2 должен идти раньше IntegerLiteral с приоритетом 3, иначе `12.5` распадётся на `12` и точку. Отдельного maximal munch нет.

🐛 Генератор падал с CS8032 при `dotnet build /p:UseSharedCompilation=false` (так собирает BenchmarkDotNet). Лечится заменой ProjectReference с OutputItemType="Analyzer" на подключение исходников через Compile — но тогда определения токенов обязаны собираться и под netstandard2.0.

Читаем:
В статье — разбор, как через static abstract member интерфейса отдать регулярку из Infrastructure в Domain без разворота зависимостей слоёв, плюс бенчмарки Compiled vs Generated и трассировка лексера через флаг --dump.

https://habr.com/ru/articles/1087280/

@aStateOfNet


System Design для начинающих: всё, что вам нужно. Часть 1

#SystemDesign #DistributedSystems #Backend

✅
В распределённой системе сетевое разделение неизбежно, поэтому реальный выбор всегда между CP и AP, а не между всеми тремя гарантиями CAP.

📈
Знаешь, что «P» присутствует всегда — и на собеседовании обосновываешь выбор БД через компромисс между согласованностью и доступностью под конкретный домен.

🔹 Банкинг, платежи, биржи требуют consistency (нельзя показывать противоречивые данные); соцсети выбирают availability (расхождение в счётчике лайков допустимо).

🔹 Оценка ресурсов «на коленке»: 10k RPS × 10 мс = 100 000 мс/с, при 1000 мс/с на ядро это 100 ядер, то есть 25 серверов по 4 ядра.

Читаем:
В статье разобраны конкретные примеры расчёта нагрузки и хранилища на кейсе Twitter (DAU, объём в ПБ/день), различия вертикального и горизонтального масштабирования и механика автоскейлинга по порогу загрузки CPU.

https://habr.com/ru/articles/873388/

@aStateOfNet






LLM предлагает, код исполняет: как разделить reasoning и side effects в production‑пайплайне
#LLM #AIAgents #DotNet

✅
Модель имеет право предлагать, код — делать, а между ними всегда остаётся строка в логе, понятная инженеру через месяц без самой модели.

📈
Вынос side effects из agent loop за typed proposal и явные гейты даёт проверяемую границу ответственности при разборе инцидентов.

🔁 Timeout сам по себе не разрешает retry: его делят минимум на четыре исхода, и при «ответ потерян» / «частичный ответ отдан» слепой повтор запрещён даже с idempotency key — сначала read‑back или reconciliation.

📊 Порог confidence калибруют на размеченных данных: в их детекторе 0,2 давал precision 1,0, а 0,5 терял около половины recall — переносить это число нельзя.

Читаем:
Внутри — код гейтов handle_refund, полная state machine записи с состоянием outcome_unknown, разбор четырёх контуров (offline eval / release gate / runtime gate / monitoring) и провальные итерации вроде инспектора tool‑call, блокировавшего вызовы по имени инструмента.

https://habr.com/ru/companies/agima/articles/1086332/

@aStateOfNet




Native AOT заставил меня переписать весь .NET проект

#dotnet #NativeAOT #csharp

✅
Переход на Native AOT — это не флаг в .csproj, а отказ от reflection-зависимостей по всему проекту.

📈
Чтобы поставлять C#-утилиту нативным бинарником без установки .NET Runtime, придётся заменить всё, что решает поведение в рантайме, на генерацию кода при сборке.

🔧 Reflection-зависимости пришлось выпилить одну за другой: System.CommandLine.Hosting, Scrutor (декораторы переехали на keyed services с [FromKeyedServices]), а сериализация — на source-generated JsonSerializerContext с явным списком CLR-типов.

⚠️ Убрав хост, потеряли Dispose service provider’а — процесс завершался до того, как асинхронный вывод логов долетал до консоли. Лечится через using var serviceProvider.

Читаем:
В статье разобрана финальная модель поставки в .NET 10 — пять NuGet-пакетов за одним именем команды, порядок публикации, managed fallback через RID any и почему Native AOT требует отдельной GitHub Actions job на каждую ОС.

https://habr.com/ru/articles/1085386/

@aStateOfNet




Adding experimental Device Bound Session Credentials (DBSC) support to ASP.NET Core Identity in .NET 11

#dotnet #aspnetcore #security

✅
DBSC привязывает сессию к TPM-ключу устройства: украденную куку нельзя использовать на чужой машине, потому что refresh требует подписи приватным ключом, который никогда не покидает TPM.

📈
Долгоживущая auth-кука заменяется на короткоживущую session-куку плюс refresh-куку, ограниченную путём /.well-known/dbsc.

🔧 Включение сводится к одному вызову AddDeviceBoundSession(sourceScheme) поверх существующей cookie-схемы; для Identity, где AddAuthentication/AddCookie спрятаны в хелперах, обходят это через new AuthenticationBuilder(builder.Services).AddDeviceBoundSession(IdentityConstants.ApplicationScheme).

⚠️ Registration- и refresh-запросы не видны в обычной вкладке Network в Chrome, требуют HTTPS, а ad blocker их ломает — отлаживать приходится по серверным логам ASP.NET Core.

Читаем:
Подробный разбор внутренностей — PolicySchemeHandler для подмены схемы, генерация challenge через Data Protection без хранения состояния, и пошаговый ход registration/refresh с 403-челленджем и проверкой публичного ключа из куки.

https://andrewlock.net/exploring-the-dotnet-11-preview-8-experimental-support-for-device-bound-session-credentials-in-aspnetcore/

@aStateOfNet





20 ta oxirgi post ko‘rsatilgan.