es
Feedback
Библиотека шарписта | C#, F#, .NET, ASP.NET

Библиотека шарписта | C#, F#, .NET, ASP.NET

Ir al canal en Telegram

Все самое полезное для C#-разработчика в одном канале. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/b60af5a4 Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead

Mostrar más

📈 Análisis del canal de Telegram Библиотека шарписта | C#, F#, .NET, ASP.NET

El canal Библиотека шарписта | C#, F#, .NET, ASP.NET (@csharpproglib) en el segmento lingüístico de Ruso es un actor destacado. Actualmente la comunidad reúne a 21 873 suscriptores, ocupando la posición 6 218 en la categoría Tecnologías y Aplicaciones y el puesto 30 852 en la región Rusia.

📊 Métricas de audiencia y dinámica

Desde su creación el невідомо, el proyecto ha mostrado un crecimiento acelerado, reuniendo a 21 873 suscriptores.

Según los últimos datos del 09 junio, 2026, el canal mantiene una actividad estable. En los últimos 30 días la variación de miembros fue de -88, y en las últimas 24 horas de -7, conservando un alto alcance.

  • Estado de verificación: No verificado
  • Tasa de interacción (ER): El promedio de interacción de la audiencia es 11.86%. Durante las primeras 24 horas tras publicar, el contenido suele obtener 7.09% de reacciones respecto al total de suscriptores.
  • Alcance de las publicaciones: Cada publicación recibe en promedio 2 594 visualizaciones. En el primer día suele acumular 1 550 visualizaciones.
  • Reacciones e interacción: La audiencia responde de forma activa: el promedio de reacciones por publicación es 9.
  • Intereses temáticos: El contenido se centra en temas clave como .net, шарписта, навигация, await, string.

📝 Descripción y política de contenido

El autor describe el recurso como un espacio para expresar opiniones subjetivas:
Все самое полезное для C#-разработчика в одном канале. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/b60af5a4 Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead

Gracias a la alta frecuencia de actualizaciones (últimos datos recibidos el 10 junio, 2026), el canal mantiene la vigencia y un amplio alcance. La analítica demuestra que la audiencia interactúa activamente con el contenido, lo que lo convierte en un punto de referencia dentro de la categoría Tecnologías y Aplicaciones.

21 873
Suscriptores
-724 horas
-187 días
-8830 días
Archivo de publicaciones
Урок истории До .NET 4.5 асинхронный код писали вручную через обратные вызовы и явный маршалинг между потоками. Это было сложно, многословно и доступно только опытным разработчикам. C# 5 и Visual Basic с новым синтаксисом async/await убрали эту сложность. Что происходит внутри Компилятор превращает каждый async-метод в конечный автомат. Вот простой пример:
public static async Task SimpleBodyAsync() {
    Console.WriteLine("Hello, Async World!");
}
После компиляции это превращается примерно в:
public static Task SimpleBodyAsync() {
    var d = new <SimpleBodyAsync>d__0();
    d.<>t__builder = AsyncTaskMethodBuilder.Create();
    d.MoveNext();
    return d.<>t__builder.Task;
}
Плюс отдельная struct с методом MoveNext, блоком try/catch и полями для хранения состояния. JIT не сможет встроить такой метод по месту вызова. Появляются издержки на вызов методов инфраструктуры SetResult, SetException и запись в поля конечного автомата. Когда async лишний Если метод всегда выполняется синхронно, оборачивать его в async нет смысла. Тауб приводит пример MemoryStream.ReadAsync: чтение из памяти и без того быстрое, и каждый вызов будет создавать новый объект Task<int> просто чтобы вернуть число. Решение: возвращать кешированный результат вручную.
private Task<int> m_lastTask;

public override Task<int> ReadAsync(
    byte[] buffer, int offset, int count,
    CancellationToken cancellationToken)
{
    int numRead = this.Read(buffer, offset, count);
    return m_lastTask != null && numRead == m_lastTask.Result
        ? m_lastTask
        : (m_lastTask = Task.FromResult(numRead));
}
Это позволяет при повторяющихся вызовах с одним и тем же результатом не создавать новые объекты совсем. SynchronizationContext и лишние переходы По умолчанию await захватывает текущий SynchronizationContext и возвращает продолжение в него. Для UI-потока это удобно: не нужно вручную делать маршалинг. Но в библиотечном коде это создаёт лишние переходы между потоками. Если из UI-потока запустить цикл копирования с await на каждой операции чтения и записи мегабайта данных, получится более 500 переходов из фоновых потоков обратно в UI-поток. Чтобы этого избежать, в библиотеках следует использовать ConfigureAwait(false):
while ((numRead = await source.ReadAsync(buffer, 0, buffer.Length)
    .ConfigureAwait(false)) > 0)
{
    await destination.WriteAsync(buffer, 0, numRead)
        .ConfigureAwait(false);
}
Без ConfigureAwait(false) в библиотечном коде возможна и взаимоблокировка: если вызывающий код в UI-потоке зовёт t.Wait(), а продолжение пытается вернуться в тот же заблокированный поток, оба будут ждать друг друга бесконечно. Локальные переменные и сбор мусора Компилятор поднимает все локальные переменные асинхронного метода в поля конечного автомата, который упаковывается в кучу при первом реальном ожидании. На момент выхода статьи компиляторы поднимали иногда больше переменных, чем нужно: даже те, что после await уже не читались.
// Лишнее поле в конечном автомате
public static async Task FooAsync() {
    var dto = DateTimeOffset.Now;
    var dt  = dto.DateTime;
    await Task.Yield();
    Console.WriteLine(dt);
}

// Лучше так:
public static async Task FooAsync() {
    var dt = DateTimeOffset.Now.DateTime;
    await Task.Yield();
    Console.WriteLine(dt);
}
Чем больше объектов создаётся, тем чаще срабатывает сборщик мусора. Это влияет на всю систему, а не только на конкретный метод. Меньше await — лучше Каждое await-выражение несёт накладные расходы. Если нужно подождать несколько задач, лучше объединить их через Task.WhenAll, чем ждать по одной:
// Хуже: три отдельных await
int ra = await a;
int rb = await b;
int rc = await c;

// Лучше: одно await на все три
int[] results = await Task.WhenAll(a, b, c);
async/await упростил жизнь разработчикам, но не отменил необходимость понимать, что происходит внутри. ➡️ Блог разработчиков 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #sharp_view

🗓️ Уже через пару часов стартует вебинар! Тема:
Как эффективно управлять контекстным окном LLM в мультиагентных системах и не сливать бюджет на токены
Ждем вас сегодня в 19:00 по московскому времени. Не пропустите начало, будет много практики! 👉 Успей занять место

Смотрите, какую годноту нашли. Заказать вряд ли получится, но вдохновиться — вполне. Это лимитированные ремни, но есть ещё ко
Смотрите, какую годноту нашли. Заказать вряд ли получится, но вдохновиться — вполне. Это лимитированные ремни, но есть ещё кое-что более лимитированное — места на нашем курсе по разработке ИИ агентов! До 30 апреля осталось всего 4 места. 👉 Занять место по ссылке 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #garbage_collector

#️⃣ hh.ru + Госуслуги + трудовая HeadHunter планомерно вводит верификацию соискателей через государственные базы данных. Резю
#️⃣ hh.ru + Госуслуги + трудовая HeadHunter планомерно вводит верификацию соискателей через государственные базы данных. Резюме без подтверждённого опыта будут скрываться алгоритмами, а аккаунты с расхождениями между резюме и трудовой уходят в теневой бан. Сильнее всего это бьёт по IT: здесь много фриланса, совмещений и серых периодов. ➡️ Куда скрутить накрученный опыт 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта

🌐 REST DTO не должны попадать в сервис Частая ошибка в .NET-проектах — использовать одни и те же модели на всех слоях. Контроллер принял CreateUserRequest, передал его прямо в сервис, сервис вернул UserResponse обратно в контроллер. Выглядит просто. Но это ловушка. REST DTO это контракт с клиентом. Сервисный слой это бизнес-логика. Это разные ответственности. Пример плохого кода:
// REST DTO
public class CreateUserRequest
{
    public string Email { get; set; }
    public string Password { get; set; }
    public string Role { get; set; } // "admin", "user"
}

// Контроллер передаёт REST DTO прямо в сервис
[HttpPost]
public async Task<IActionResult> Create(CreateUserRequest request)
{
    var user = await _userService.CreateAsync(request); // не надо
    return Ok(user);
}

// Сервис знает о REST DTO
public async Task<UserResponse> CreateAsync(CreateUserRequest request)
{
    // бизнес-логика завязана на HTTP-контракт
}
Теперь представьте, что появился второй клиент. Он шлёт другой формат. Или вы хотите вызвать CreateAsync из фоновой задачи, где нет никакого HTTP-запроса. Приходится либо тащить ненужный DTO, либо переписывать сервис. Ещё хуже поле Role в CreateUserRequest. Клиент сам указывает, кем хочет стать. Даже если оно не используется или перезаписывается далее, выглядит это не очень. Как правильно Каждый слой работает со своей моделью. Контроллер маппит входящий DTO в команду или модель сервисного слоя. Сервис возвращает доменный результат. Контроллер маппит его в ответный DTO:
// REST DTO — только для HTTP-слоя
public class CreateUserRequest
{
    public string Email { get; set; }
    public string Password { get; set; }
    // Role здесь нет — клиент не решает
}

public class UserResponse
{
    public Guid Id { get; set; }
    public string Email { get; set; }
    public string Role { get; set; }
}

// Сервисная модель — внутренний контракт
public class CreateUserCommand
{
    public string Email { get; set; }
    public string Password { get; set; }
    public UserRole Role { get; set; } // enum, не строка
}

public class UserResult
{
    public Guid Id { get; set; }
    public string Email { get; set; }
    public UserRole Role { get; set; }
}
// Контроллер — маппит и не лезет в логику
[HttpPost]
public async Task<IActionResult> Create(CreateUserRequest request)
{
    var command = new CreateUserCommand
    {
        Email = request.Email,
        Password = request.Password,
        Role = UserRole.User // роль задаётся здесь, не клиентом
    };

    var result = await _userService.CreateAsync(command);

    var response = new UserResponse
    {
        Id = result.Id,
        Email = result.Email,
        Role = result.Role.ToString()
    };

    return Ok(response);
}

// Сервис — ничего не знает про HTTP
public async Task<UserResult> CreateAsync(CreateUserCommand command)
{
    // чистая бизнес-логика
}
Почему это важно Сервис перестаёт зависеть от формата HTTP-запроса. Его можно вызвать из воркера, gRPC-эндпоинта, теста без изменений. Контракт с клиентом можно менять независимо от внутренней логики. Добавили поле в CreateUserRequest, сервис об этом не знает. Валидация и маппинг живут в одном месте — в контроллере. Сервис получает уже проверенные данные в нужном формате. Для маппинга удобно использовать AutoMapper или Mapster, но даже ручной маппинг лучше, чем слитые слои. Граница между HTTP-слоем и бизнес-логикой — это не формальность. Это то, что позволяет менять одно, не трогая другое. 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #il_люминатор 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #il_люминатор

📰 Любимый автор по C# Кого смотрите и читаете по C# и .NET? Авторы на YouTube, Хабре, телеграм-каналы, подкасты — пишите в к
📰 Любимый автор по C# Кого смотрите и читаете по C# и .NET? Авторы на YouTube, Хабре, телеграм-каналы, подкасты — пишите в комменты 💬 Мы, естественно, следим за Ником Чапсасом, но может есть менее гигантские медиа-личности? 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #entry_point

🔥 База по ИИ-агентам от научного сотрудника Сколтеха и НИУ ВШЭ Знакомьтесь, Екатерина Трофимова. Кандидат компьютерных наук,
🔥 База по ИИ-агентам от научного сотрудника Сколтеха и НИУ ВШЭ Знакомьтесь, Екатерина Трофимова. Кандидат компьютерных наук, ресерчер в Центре ИИ Сколтеха и лаборатории LAMBDA. Она объединяет глубокую академическую экспертизу и практику: знает, как ИИ-системы устроены «под капотом» и как встроить их в реальные проекты (в т.ч. для Т-банка). Мы попросили Екатерину собрать список мастхев материалов для тех, кто хочет проектировать агентов в проде. Сохраняйте список. 🛠 Стек и фреймворки: DSPy — алгоритмическая оптимизация промптов (вместо ручного подбора слов). Semantic Kernel и LangMem — инструменты для управления сессионной и долгосрочной памятью. MCP (Model Context Protocol) — новый стандарт от Anthropic для подключения агентов к вашим БД и локальным файлам. 📖 Документация, которую нужно знать: Anthropic Prompt Caching — как кэшировать контекст и радикально резать косты на API. OpenAI Agents SDK / Cookbook — лучшие практики работы с памятью. Augment — платформа для оптимизации работы ИИ-агентов и контроля токенов. 🔬 Хардкорные статьи и препринты (на выходные): Lost in the Middle — почему LLM «слепнут» на длинных текстах и забывают середину контекста. How Do Coding Agents Spend Your Money? — куда улетает бюджет при работе автономных кодинг-агентов. MemGPT — архитектура операционной системы для LLM с иллюзией бесконечной памяти. InjecAgent / AgentSentry — всё о безопасности и защите агентов от инъекций в промпты. Екатерина Трофимова — один из ключевых экспертов нашего курса AgentOps. На своих лекциях она детально разбирает, как проектировать инструменты для агентов, как агент принимает решения о вызове инструментов и какие ограничения возникают в реальном проде 🎁 Акция в честь старта продаж! Прямо сейчас при покупке Инженерного трека вы получаете полный доступ к материалам курса «Разработка ИИ-агентов» в подарок. 👉 Забрать 2 курса по цене 1 и начать обучение

🔄 Мелкий релиз с нужными правками API Вышел релиз v6.2.0 библиотеки OrionGuard для валидации в .NET. Версия небольшая, но ус
🔄 Мелкий релиз с нужными правками API Вышел релиз v6.2.0 библиотеки OrionGuard для валидации в .NET. Версия небольшая, но устраняет несколько неудобств, которые вылезли при написании демо к v6.1.0. Что добавили Появился интерфейс IStronglyTypedId<TValue> — теперь strongly-typed ID на основе record и ID, сгенерированные source-генератором через struct, работают под одним контрактом. Guards и DI-хелперы принимают оба варианта без дополнительного кода. Добавили абстрактный record DomainEventBase. Раньше в каждом доменном событии нужно было вручную объявлять EventId и OccurredOnUtc. Теперь достаточно унаследоваться:
public sealed record OrderShippedEvent(OrderId OrderId) : DomainEventBase;
Сгенерированные struct-идентификаторы теперь реализуют IParsable<TSelf> и ISpanParsable<TSelf>. Это значит, что route-биндинг в ASP.NET Core minimal API работает из коробки без кастомных конверторов. Source-генератор стал умнее: он проверяет во время компиляции, подключён ли EF Core, и генерирует ValueConverter<,> только если да. Лишний код в проект не попадает. Что изменили Все sub-пакеты переименованы — префикс Moongazing. убрали из NuGet PackageId: Moongazing.OrionGuard.AspNetCoreOrionGuard.AspNetCore Moongazing.OrionGuard.MediatROrionGuard.MediatR Moongazing.OrionGuard.GeneratorsOrionGuard.Generators И так далее для Swagger, OpenTelemetry, Blazor, Grpc, SignalR. C#-пространства имён и структура папок не изменились. Существующие using-директивы продолжают работать. Миграция с v6.1.0 В .csproj нужно обновить ссылки на пакеты: заменить Moongazing.OrionGuard.X на OrionGuard.X. Код трогать не нужно. Если хочется убрать бойлерплейт в доменных событиях — можно добавить : DomainEventBase, но это опционально. ➡️ Источник 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #async_news

🛠 От горизонтальных слоёв к вертикальным срезам Структура Controllers / Services / Models настолько привычна, что мы перестали её замечать. Но она главный источник боли по мере роста проекта. Чтобы разобраться в одной фиче, приходится прыгать между тремя папками. Когда фича сложная код размазан по десятку файлов, и никто не держит в голове полную картину Проблема: классические слои Фича в типичном проекте выглядит так:
Controllers/ProductsController.cs
Services/IProductService.cs
Services/ProductService.cs
Models/CreateProductRequest.cs
Каждый из файлов знает только о своём слое. Хотите понять одну операцию — обходите весь проект. Вертикальные срезы Принцип простой: код, который меняется вместе живёт вместе. Вместо горизонтальных слоёв на весь проект вертикальные срезы: один срез на одну фичу. Всё нужное в одной папке:
Features/
  Products/
    CreateProduct/
      CreateProduct.cs   ← всё здесь
Один файл содержит команду, валидацию и обработчик:
public static class CreateProduct
{
    // Входные данные
    public record Command(string Name, decimal Price) : IRequest<Guid>;

    // Валидация
    public class Validator : AbstractValidator<Command>
    {
        public Validator()
        {
            RuleFor(x => x.Name).NotEmpty();
            RuleFor(x => x.Price).GreaterThan(0);
        }
    }

    // Логика
    internal class Handler(AppDbContext db) : IRequestHandler<Command, Guid>
    {
        public async Task<Guid> Handle(Command req, CancellationToken ct)
        {
            var product = new Product { Name = req.Name, Price = req.Price };
            db.Products.Add(product);
            await db.SaveChangesAsync(ct);
            return product.Id;
        }
    }
}
Пример использует MediatR для pipeline и Carter для регистрации. Они удобны, но не обязательны. Главное — положение кода Что получаем Высокая связность. Новый разработчик открывает одну папку и видит фичу целиком — от входа до выхода. Низкое зацепление. Изменения в одном срезе не затрагивают другие. Никакого страха "а вдруг сломается что-то в другом месте". Конец раздутым сервисам. IProductService неизбежно превращается в класс на тысячу строк с двадцатью ответственностями. Handler делает ровно одно дело. А что с дублированием Между срезами иногда появляется похожий код. Это нормально. Цена небольшого дублирования несравнимо меньше, чем цена высокого зацепления и низкой связности, которые приносят горизонтальные слои. Долгосрочно это огромная победа. 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #sharp_view

🤩 .NET 10 в Ubuntu 26.04 Вышел Ubuntu 26.04 LTS. Вместе с ним официальная поддержка .NET 10 прямо из стандартного репозитори
🤩 .NET 10 в Ubuntu 26.04 Вышел Ubuntu 26.04 LTS. Вместе с ним официальная поддержка .NET 10 прямо из стандартного репозитория. Две команды и SDK готов:
sudo apt update
sudo apt install dotnet-sdk-10.0
Проверить, что всё работает:
dotnet run - << 'EOF'
using System.Runtime.InteropServices;

Console.WriteLine($"Hello {RuntimeInformation.OSDescription} from .NET {RuntimeInformation.FrameworkDescription}");
EOF
Вывод будет примерно таким:
Hello Ubuntu Resolute Raccoon from .NET .NET 10.0.5
Это так называемый file-based app. Он передаётся через stdin напрямую в dotnet run. Стандартный unix-подход. Контейнеры Образы с тегом resolute уже доступны. Если вы использовали -noble, достаточно поменять суффикс:
sed -i "s/noble/resolute/g" Dockerfile.chiseled
После этого собрать и запустить с ограничениями ресурсов:
docker build --pull -t aspnetapp -f Dockerfile.chiseled .
docker run --rm -it -p 8000:8080 -m 50mb --cpus .5 aspnetapp
Chiseled-образы остались, ничего не убрали Native AOT Если нужен маленький бинарь с быстрым стартом — dotnet-sdk-aot-10.0 теперь в репозитории Ubuntu. Установка:
apt install -y dotnet-sdk-aot-10.0 clang
Публикация простого приложения:
dotnet publish app.cs
Результат:
1.4M    artifacts/app/app
3.0M    artifacts/app/app.dbg
Запуск занял 3 миллисекунды. Для веб-сервиса с PublishAot=true итоговый размер около 13 МБ вместе с метаданными System.Text.Json. .NET 8 и 9 не входят в основной репозиторий Ubuntu 26.04, но доступны через отдельный PPA от Canonical:
apt install -y software-properties-common
add-apt-repository ppa:dotnet/backports
После подключения появятся пакеты dotnet-sdk-8.0, dotnet-sdk-9.0 и соответствующие aspnetcore-runtime-*. Поддержка там на уровне "best-effort", то есть официально, но без гарантий уровня LTS. Что важно Ubuntu 26.04 принёс три заметных изменения на уровне ОС: • Linux 7.0 — команда .NET начнёт тестирование, как только получит VM. • Постквантовая криптография — поддержка уже есть в .NET 10. • Удаление cgroup v1. Переход на cgroup v2 в .NET сделали несколько лет назад, так что сломаться ничего не должно. Если переходите на Ubuntu 26.04 в продакшне, то всё основное готово с первого дня. ➡️ Блог разработчиков 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #async_news

🛠 Структуры не всегда быстрее Распространённое заблуждение среди разработчиков на C#, что структуры всегда эффективнее класс
🛠 Структуры не всегда быстрее Распространённое заблуждение среди разработчиков на C#, что структуры всегда эффективнее классов. На самом деле это работает только для маленьких структур. Структуры живут на стеке и не создают нагрузку на GC. Но у этого есть цена: при каждой передаче структуры в метод или присваивании создаётся полная копия. Для маленьких структур это быстро. Для больших не очень. Вот пример структуры, которая выглядит безобидно, но копирует 64+ байт при каждом вызове:
// Копируется целиком при каждой передаче
public struct BigOrderStruct
{
    public int Id;
    public string Customer;
    public decimal Total;
    // ... ещё 12 полей
    public List<Item> Items; // это уже ссылочный тип
}
Обратите внимание на List<Items>. Как только структура содержит ссылочные типы, часть преимуществ стека теряется, ведь ссылка всё равно уходит в кучу. Для маленьких значений без ссылочных типов readonly record struct идеален:
public readonly record struct SmallOrderId(int Id);
Для всего остального readonly record class чаще выигрывает и по читаемости, и по производительности за счёт лучшего поведения кэша. 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #il_люминатор

👋 Небольшой вопрос для прокачки Это классика технических интервью в C#: Статический конструктор — когда именно его вызывает
👋 Небольшой вопрос для прокачки Это классика технических интервью в C#:
Статический конструктор — когда именно его вызывает рантайм
Вы не пишете new MyClass(), не обращаетесь к объекту, не делаете вообще ничего явного. А он всё равно срабатывает. ➡️ Узнайте ответ 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #dotnet_challenge

🤩 Валидация для .NET Валидация входящих данных в .NET-проектах это рутина, которую каждый решает по-своему. Кто-то пишет if
🤩 Валидация для .NET Валидация входящих данных в .NET-проектах это рутина, которую каждый решает по-своему. Кто-то пишет if (value == null) throw new ArgumentNullException(...) в каждом методе, кто-то тащит FluentValidation и настраивает его под свои нужды. OrionGuard предлагает ещё один вариант: fluent-интерфейс, поддержку ASP.NET Core, MediatR, Blazor, gRPC и SignalR. Всё в одной экосистеме. Установка базового пакета:
dotnet add package OrionGuard
Простая валидация выглядят так:
using Moongazing.OrionGuard.Core;
using Moongazing.OrionGuard.Extensions;

Ensure.That(email).NotNull().NotEmpty().Email();
Ensure.That(age).InRange(18, 120);
Если нужна производительность без аллокаций, то есть FastGuard на основе Span<T>:
FastGuard.NotNullOrEmpty(name, nameof(name));
FastGuard.Email(email, nameof(email));
Что внутри Библиотека разбита на 9 пакетов. Каждый подключается отдельно. OrionGuard — ядро. OrionGuard.AspNetCore — middleware, фильтры, интеграция с IOptions. OrionGuard.MediatR — автоматическая валидация в CQRS-пайплайне. OrionGuard.Generators — source-генераторы для компайл-тайм валидации без рефлексии. OrionGuard.Blazor — интеграция с EditForm. OrionGuard.Grpc и OrionGuard.SignalR — перехватчики для gRPC и SignalR. Несколько конкретных примеров Накопление ошибок без исключений:
var result = GuardResult.Combine(
    Ensure.Accumulate(email, "Email").NotNull().Email().ToResult(),
    Ensure.Accumulate(password, "Password").MinLength(8).ToResult()
);
if (result.IsInvalid)
    return BadRequest(result.ToErrorDictionary());
Защита от SQL-инъекций и XSS:
userInput.AgainstSqlInjection(nameof(userInput));
userInput.AgainstXss(nameof(userInput));
filePath.AgainstPathTraversal(nameof(filePath));
Вложенная валидация с путями до поля:
var result = Validate.Nested(order)
    .Property(o => o.OrderNumber, p => p.NotEmpty())
    .Nested(o => o.Customer, customer => customer
        .Property(c => c.Email, p => p.NotEmpty().Email())
        .Nested(c => c.Address, address => address
            .Property(a => a.ZipCode, p => p.NotEmpty())))
    .Collection(o => o.Items, (item, _) => item
        .Property(i => i.Quantity, p => p.GreaterThan(0)))
    .ToResult();
// Ошибки будут выглядеть так: "Customer.Address.ZipCode", "Items[2].Quantity"
Динамические правила из JSON. Для случаев, когда правила хранятся в базе или конфиге:
var json = """
{
  "Rules": [
    { "PropertyName": "Email", "RuleType": "Email" },
    { "PropertyName": "Age", "RuleType": "Range", "Parameters": { "Min": 18, "Max": 120 } }
  ]
}
""";

var validator = DynamicValidator.FromJson(json);
var result = validator.Validate(userDto);
Source-генератор для NativeAOT:
[GenerateValidator]
public sealed class CreateUserRequest
{
    [NotNull, NotEmpty, Length(3, 50)]
    public string Name { get; set; }

    [NotNull, Email]
    public string Email { get; set; }
}

// Валидатор генерируется на этапе компиляции — без рефлексии
var result = CreateUserRequestValidator.Validate(request);
Интеграция с ASP.NET Core:
// Program.cs
builder.Services.AddOrionGuardAspNetCore();

app.MapPost("/api/users", (CreateUserRequest req) => { ... })
   .WithValidation<CreateUserRequest>();
Библиотека поддерживает 14 языков для сообщений об ошибках, включая русский. Есть слой совместимости с FluentValidation, миграция сводится к замене using. Все regex-паттерны генерируются через GeneratedRegex, FrozenSet используется для O(1)-поиска в security-паттернах. Последний релиз v6.2.0 вышел 22 апреля 2026. 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #sharp_view

⚙️ Сервер тормозит без видимых причин Представьте картину: CPU загружен на 30–40%, ошибок нет, но запросы внезапно начинают т
⚙️ Сервер тормозит без видимых причин Представьте картину: CPU загружен на 30–40%, ошибок нет, но запросы внезапно начинают тормозить, а время ответа под нагрузкой улетает в небо. Это не магия и не баг в инфраструктуре. Скорее всего, вы столкнулись с голоданием пула потоков. Потоки это ваша пропускная способность. Каждый заблокированный поток это запрос, который ждёт в очереди. Когда таких потоков становится много, латентность взрывается, хотя CPU при этом спокойно отдыхает. Причина почти всегда в одном из таких паттернов:
var data = httpClient.GetStringAsync(url).Result; // блокирует поток

Task.Run(() => DoWork()).Wait(); // форсированная синхронизация
Эти вызовы берут асинхронную операцию и намеренно блокируют поток до её завершения. В результате поток занят, но не делает ничего полезного, а просто ждёт. Попробуйте сделать так и замеряйте результат:
var data = await httpClient.GetStringAsync(url); 
📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #il_люминатор

🛠 Native AOT в Node.js: пишем аддоны на C# Исторически для платформоспецифичных задач в VS Code расширении (например, чтение реестра Windows) использовались нативные аддоны на C++. Их сборка требовала node-gyp и конкретной версии Python на каждой машине разработчика. Для .NET-команды это лишняя зависимость, которую нужно поддерживать и в CI, и при онбординге новых участников. Решение Native AOT умеет компилировать .NET-код в нативную shared library с произвольными точками входа. Node.js аддоны требуют только одного: экспортированной функции napi_register_module_v1. Native AOT с этим справляется. Как это работает Аддон взаимодействует с Node.js через [N-API](https://nodejs.org/api/n-api.html) — стабильный C-совместимый интерфейс. Язык реализации значения не имеет, важно лишь соответствие сигнатурам. Минимальный .csproj:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <PublishAot>true</PublishAot>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>
</Project>
PublishAot говорит SDK собрать нативную библиотеку при публикации. AllowUnsafeBlocks нужен из-за работы с указателями при интеропе с N-API. Точка входа модуля:
[UnmanagedCallersOnly(
    EntryPoint = "napi_register_module_v1",
    CallConvs = [typeof(CallConvCdecl)])]
public static nint Init(nint env, nint exports)
{
    Initialize();
    RegisterFunction(env, exports, "readStringValue"u8, &ReadStringValue);
    return exports;
}
Атрибут [UnmanagedCallersOnly] экспортирует метод с нужным именем и calling convention. Суффикс u8 создаёт ReadOnlySpan<byte> с UTF-8 строкой без аллокаций. Вызов N-API из .NET N-API функции экспортирует сам node.exe. Чтобы не линковаться с отдельной библиотекой, используется кастомный резолвер через NativeLibrary.SetDllImportResolver:
NativeLibrary.SetDllImportResolver(
    Assembly.GetExecutingAssembly(),
    (libraryName, assembly, searchPath) =>
        libraryName is "node"
            ? NativeLibrary.GetMainProgramHandle()
            : 0);
После этого P/Invoke с именем "node" будет резолвиться в хост-процесс:
[LibraryImport("node", EntryPoint = "napi_create_string_utf8")]
internal static partial Status CreateStringUtf8(
    nint env, ReadOnlySpan<byte> str, nuint length, out nint result);
Пример экспортируемой функции Функция читает значение из реестра Windows и возвращает его в JavaScript:
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static nint ReadStringValue(nint env, nint info)
{
    try
    {
        var keyPath = GetStringArg(env, info, 0);
        var valueName = GetStringArg(env, info, 1);

        if (keyPath is null || valueName is null)
        {
            ThrowError(env, "Expected two string arguments: keyPath, valueName");
            return 0;
        }

        using var key = Registry.CurrentUser.OpenSubKey(keyPath, writable: false);

        return key?.GetValue(valueName) is string value
            ? CreateString(env, value)
            : GetUndefined(env);
    }
    catch (Exception ex)
    {
        ThrowError(env, $"Registry read failed: {ex.Message}");
        return 0;
    }
}
Исключения нужно обязательно перехватывать: необработанное исключение в методе с [UnmanagedCallersOnly] крашит хост-процесс. ThrowError пробрасывает ошибку в JavaScript как стандартный Error. Что в итоге Команда убрала зависимость от Python и node-gyp. Теперь yarn install работает только с Node.js и .NET SDK, которые и так нужны для разработки. CI стал проще, онбординг быстрее. Производительность сопоставима с C++-реализацией: Native AOT генерирует оптимизированный нативный код, а для задач типа доступа к реестру и маршалинга строк разницы на практике нет. ➡️ Источник 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #sharp_view

🧑‍💻 Подборка вакансий для шарпистов Старший Backend C# ASP. NET разработчик — от 300 000 до 400 000 ₽ , удалёнка или гибрид в Москве Разработчик C# — офис в Минске Unity Developer — удалёнка ➡️ Еще больше топовых вакансий — в нашем канале C# Jobs 🐸 Библиотека шарписта

❗️ Внеплановое обновление .NET 10.0.7 Microsoft выпустила внеплановый патч для .NET 10. Причина — баг в пакете Microsoft.AspN
❗️ Внеплановое обновление .NET 10.0.7 Microsoft выпустила внеплановый патч для .NET 10. Причина — баг в пакете Microsoft.AspNetCore.DataProtection, который попал в релиз вместе с плановым обновлением 10.0.6. Что случилось После выхода 10.0.6 пользователи начали сообщать, что расшифровка данных перестала работать. Microsoft изучила проблему и обнаружила, что регрессия также открывает уязвимость CVE-2026-40372. Суть бага: управляемый энкриптор вычислял HMAC-тег не над теми байтами полезной нагрузки, а потом и вовсе выбрасывал результат. Это приводило к повышению привилегий. Затронуты версии Microsoft.AspNetCore.DataProtection от 10.0.0 до 10.0.6 включительно. Кого это касается Всех, кто использует ASP.NET Core Data Protection в приложениях на .NET 10. Это стандартный механизм защиты данных в ASP.NET Core — сессии, антифоргери-токены, куки и всё, что шифруется через IDataProtector. Что делать Обновить пакет Microsoft.AspNetCore.DataProtection до версии 10.0.7 как можно скорее:
dotnet add package Microsoft.AspNetCore.DataProtection --version 10.0.7
После обновления пакета пересобрать и перевыложить приложение. Проверить текущую версию SDK можно командой:
dotnet --info
➡️ Источник 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #async_news

🗓 В следующий вторник (28.04) в 19:00 встречаемся в онлайне. Тема:
Как эффективно управлять контекстным окном LLM в мультиагентных системах и не сливать бюджет на токены
В кружке Кирилл рассказал, какие именно подходы будем разбирать. 👉 Занять место на вебинаре