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

AS4: подписанный и зашифрованный обмен документами с гос- и корпоративными системами ЕС. Теперь нативный коннектор redb.Route, без Java MSH.
Если искать ...

Объектно-ориентированный язык программирования

AS4: подписанный и зашифрованный обмен документами с гос- и корпоративными системами ЕС. Теперь нативный коннектор redb.Route, без Java MSH.
Если искать ...

Выложу здесь в одну статью серию постов из моего ТГ канала про то, как в этом году прошёл DotNext.
25 и 26 сентября в Москве прошла очередная конференция DotNext. Вернулся и делюсь впечатлениями. Я уже писал, какие доклады хочу посетить, однако, по ходу несколько раз передумал. На этот раз решил посетить несколько воркшопов, т.к. они не записываются на видео, а доклады можно посмотреть в записи позже. Воркшоп отличается от доклада тем, что на нём разбирается конкретный практический пример, а аудитория активно участвует, задавая вопросы или предлагая решения. Раньше они меня пугали своей продолжительностью. Доклады длятся по часу, а воркшоп – это обычно от полутора часов и дольше. ИЧСХ, в начале каждого воркшопа любой докладчик считает своим долгом упомянуть, что времени у нас очень мало 😉. Хочу признать, что я много потерял, не посещая воркшопы раньше. Это отличный опыт практики и командной работы.
Первым был воркшоп от Андрея Цветцих (они с братом Денисом завсегдатаи различных конференций) «Паттерны асинхронного взаимодействия в распределенных системах». Мы разбирали пример реализации сервиса уведомлений (например, по e-mail) о событиях. Как настроить передачу информации в сервис рассылки? Какие данные передавать? Как реализовать гарантии доставки и избежать отправки дублирующих сообщений? Возможно ли реализовать доставку «exactly-once»? Интересно, что здесь нет единственно верного решения. Каждое решение – компромисс со своими плюсами и минусами.

В .NET 9 появился новый примитив синхронизации System.Threading.Lock, и многие команды начали заменять привычные object-замки на новый тип в поисках более предсказуемого поведения и производительности. Но такая замена меняет саму механику работы lock: достаточно одного места, где экземпляр потеряет свой тип, чтобы два участка кода перестали синхронизироваться между собой.
В статье разберемся, как именно устроена новая модель блокировок в C#, где компилятор помогает заметить проблему, а где она остаётся скрытой, и стоит ли переходить на Lock ради заявленных преимуществ.
Дисклеймер. Все описанное выполнялось в изолированной лаборатории на вымышленном приложении, которое я собрал специально для статьи. Совпадения имен с реальными продуктами случайны. Материал образовательный — для AppSec, .NET-разработчиков и всех, кому интересно, как «сохранить объект в файл» превращается в удаленное выполнение кода. Не применяйте это к системам, на которые у вас нет письменного разрешения.
Есть распространенный страх: анализ защищенности — это что-то темное, для избранных, с ассемблером и магией. На деле большая часть работы — это чтение кода по понятным правилам. Сейчас разберем реальный по механике баг (небезопасная десериализация в .NET → выполнение произвольной команды) так, будто читаем букварь: сначала буквы, потом слоги, потом целые слова.
К концу статьи вы будете уметь: декомпилировать .NET-приложение, находить в нем точки сериализации и десериализации, читать свойства и сеттеры, отличать безобидный класс от «gadget» и по одной строчке понимать, есть ли уязвимость. Все воспроизводится на приложенном стенде.
Про цвет. Листинги с подсветкой даны картинками. Цвета: желтый — имя класса/тип, синий — имя свойства, зеленый — значение (данные), красный — опасное место (sink, уязвимая строка, побочный эффект). В «было/стало» красный — что убираем, зеленый — что ставим.

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

Это продолжение истории про AGR.Checker — плагин для Revit, который мы с Андреем Прохоровым сделали для проверки ЦИМ на соответствие требованиям IDS перед сдачей в составе АГР. В прошлой статье я рассказывала про архитектуру, лицензирование и регистрацию программы в Роспатенте. С тех пор плагин поработал на реальных объектах, и почти всё новое в этой версии выросло из одного вопроса: «а почему вот это приходится проверять руками?»
Ниже — что мы добавили и какие инженерные задачи пришлось решить по дороге.
Экспорт в IFC с постобработкой
В конце прошлой статьи я обещала выгрузку IFC для последующей загрузки на СтроимПросто. Сделали.
Экспорт идёт через стандартное окно Revit: мы сознательно не стали собирать настройки экспорта программно. Недокументированные опции IFC-экспортёра вели себя по-разному в разных версиях Revit, и надёжнее оказалось дать пользователю привычное окно со своей конфигурацией и файлом маппинга.
Самое интересное начинается после экспорта. Плагин подписан на событие Revit о завершении экспорта и сразу же обрабатывает готовый IFC-файл:

Я написал source generator для лексера HydraScript. Он собирал регулярное выражение из описаний токенов. После этого мне оставалось скопировать регулярку и руками вставить её в другой файл.
Даже тест на забытое копирование пришлось завести. Довольно много усилий, чтобы поддерживать одну строку в актуальном состоянии.
Проблема была в том, что результат моего генератора требовался другому генератору — тому, который стоит за атрибутом [GeneratedRegex] в .NET. Хотелось описать токен один раз и поручить остальное сборке. Для этого пришлось разобраться, в каком порядке Roslyn вообще выполняет генераторы.

Поставил MelonLoader в демку на Unity 6, собрал мод — и он молчит: BadImageFormatException: Duplicate type with name '<>O'. За выходные я написал два чит-мода с почти одинаковым ТЗ: меню для Valheim (Mono, BepInEx) и трейнер для демки Go Next! (IL2CPP, MelonLoader). Код получился настолько разный, что набралось восемь мест, где всё ломается именно из-за отсутствия managed-кода: битая сгенерированная сборка, вырезанный GUILayout, падающий Input.GetKeyDown, цена интеропа. Плюс разбор того, где Unity на самом деле держит PlayerPrefs и почему SetGodMode() хуже собственного патча.

HydraScript — мой интерпретатор на C#. В нём есть объекты, статическая типизация и вызовы методов. А class, interface и constructor я вообще не добавлял.
Точке на плоскости нужны две координаты, для вычисления расстояния до другой точки нужна функция. Хотелось, чтобы эти вещи работали без обязательной упаковки в класс.
Тут я вдохновлялся сразу двумя языками: структурной типизацией TypeScript и методами с явным получателем из Go. Подход Go заодно сильно упрощал жизнь мне самому — автору языка. Функции у меня уже работали, и методы можно было построить на их основе, не вдаваясь в реализацию классов.

Поле описания merge request пустое или содержит список commit messages. Ревьюер открывает diff на несколько десятков файлов и не понимает, с чего начать. Писать описание руками — рутина, на которую забивают, а задача при этом формализуется идеально: на входе diff, на выходе текст по шаблону. Идеальная работа для LLM.
Так появился AI Describer — CLI‑инструмент, который запускается одним шагом пайплайна, берёт git diff относительно целевой ветки, отдаёт его модели и публикует структурированное описание обратно в merge/pull request. Работает и в GitLab CI, и в Jenkins на Windows‑агентах: у нас два хостинга репозиториев и столько же систем сборки.

Мне хотелось, чтобы знакомство с HydraScript выглядело просто: скачал интерпретатор, запустил скрипт. Сомнительно предлагать человеку сначала установить подходящий .NET Runtime ради моего языка программирования.
Переход на Native AOT поначалу казался правкой .csproj. Включил настройку, опубликовал бинарник, пошёл дальше. На практике пришлось переписать всё. Досталось даже интеграционным тестам. А в процессе рефакторинга ещё и пропали логи.

Коннектор Redis для redb.Route: все структуры данных как шаги маршрута, Pub/Sub, Streams с группами потребителей, список как очередь и Claim Check на Redis.
В интеграционном проекте Redis редко бывает чем-то одним. Это кэш ответов внешних систем, счётчики и блокировки, шина событий через Pub/Sub, журнал сообщений через Streams, очередь задач на списке. Обычно каждая из этих ролей живёт в своём куске кода со своим подключением, своими повторами и своим логированием.
В redb.Route все они собраны в одном коннекторе redb.Route.Redis. Любая операция Redis становится шагом маршрута, а подписка, стрим или список становятся его входом. Подключение, телеметрия и плавная остановка при этом общие с остальными коннекторами. Под капотом StackExchange.Redis 2.11.8.
Потребители Redis описаны здесь начиная с ...

Мой язык программирования HydraScript, написанный на C#, уже умел выполнять такой код:
let x = 10 x = x + 1 >>> x
Получаем 11; >>> — это оператор вывода. Присваивание работает, сложение работает. Захотелось добавить привычное сокращение:
let x = 10 x += 1 >>> x
Результат должен остаться тем же — 11. Зачем учить весь компилятор ещё одной операции, если всё необходимое у него уже есть? Можно прямо в парсере превратить x += 1 в дерево для x = x + 1.
Эта часть уместилась в несколько строк. Основная работа досталась методу Clone().

Два фронтенда, один C#-мозг — что действительно различается, что похоже сильнее, чем принято считать, и что говорит рынок труда.
До 2019 года моим фронтендом были семантический HTML и CSS. Div'ы, классы, разметка, которую я мог удержать в голове. Я был бэкенд-разработчиком, и меня это вполне устраивало.
Потом случились две вещи. Компания попросила меня залезть в проект на Vue, а Microsoft выпустила Blazor. В следующие годы я плотно поработал с обоими: с Vue — у двух работодателей, с Blazor — на личном проекте, который до сих пор работает.
Это не статья с бенчмарками. Это про то, как эти две технологии ощущаются, когда мышление у тебя C#-овое.

Маршрут в .route.xml на том же движке, что и C#: подсказки в VS Code, проверка пакета в dotnet build, граф-редактор и горячая замена модуля.
Интеграционный маршрут живёт дольше, чем код вокруг него. Партнёр переезжает в другую папку на SFTP, аналитик просит отдельную ветку под новый тип файла, поддержка хочет понять, каким путём прошла заявка, которую ночью отклонили. Всё это вопросы к маршруту, и отвечать на них через C#-проект, сборку и выкладку модуля выходит дороже, чем сам вопрос.
В 4.0 у redb.Route появилась вторая запись маршрута: XML. Файл .route.xml кладётся в пакет, пакет в папку работающего сервиса, и меньше чем через полсекунды по новому маршруту идёт первое сообщение. При этом ...

Привет! Меня зовут Александр Илларионов, я бэкенд‑разработчик в Altenar. Мы работаем на довольно сложном и при этом очень интересном рынке: собираем данные о спортивных матчах со всего мира — например, время и место их проведения, описания чемпионатов и участников, а также вероятности наступления ключевых событий вроде голов, аутов и пенальти. И всё это в live‑режиме.
В этой статье рассказываю, как мы переосмыслили роль Heartbeat‑компонента в нашей системе — от обычного сигнала до инструмента, который реально управляет работой всей системы. Наш опыт будет полезен тем, кто работает с .NET, распределёнными системами или строит инфраструктуру под высоконагруженные real‑time потоки данных.

Пустой наследник Random работает медленнее родителя. string[] и object[] оказываются одним объектом. А Matrix4x4 до сих пор держит шестнадцать полей float вместо четырёх векторов.
Ни одно из трёх уже не переделать, и на то есть причины.

В System.Random кто-то двадцать лет назад написал 21 вместо 31. Опечатку заметили, разобрали и оставили как есть — исправить, и new Random(42) начнёт выдавать другие числа.
А ещё new Random() и new Random(42) — вообще разные генераторы. Разница на некоторых методах доходит до 10 раз.

Три атрибута, которые меняют результат компиляции. Один запускает метод раньше Main. Второй передаёт текст выражения вместо результата. Третий ускоряет stackalloc в 50 раз.
Все три описаны в документации и применяются внутри .NET. А в рабочем коде я их ни разу не видел.

Мажор всей экосистемы: что появилось в redb.Core, redb.Route, redb.Tsak и redb.Identity, что закрыто по безопасности и что ломает обновление.
Много работы было сделано ночами при свечах. Обратная связь от людей давала пищу для размышлений и фиксов: комментарии к статьям, обсуждения на GitHub, отчёты с живых стендов. И вот, отработав по-стахановски, выпускаю 4.0.0.
Это мажор всей экосистемы разом. Хранилище redb.Core, интеграционный движок redb.Route, рантайм redb.Tsak и OpenID-сервер redb.Identity выходят одним номером: 76 пакетов на NuGet (у 3.7 было 66), семь образов в GHCR, архивы под Windows и Linux. Pro-издание по-прежнему бесплатно и не требует лицензионного ключа на всей линии 4.x.
Изменений столько, что подробный разбор любого из продуктов тянет на отдельную статью, и такие статьи будут. Здесь коротко, почти списком: что появилось, что закрыто по безопасности и что нужно знать перед обновлением. Полные списки изменений лежат на redb.ru/releases.