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

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

الذهاب إلى القناة على Telegram

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

إظهار المزيد

📈 نظرة تحليلية على قناة تيليجرام Библиотека шарписта | C#, F#, .NET, ASP.NET

تُعد قناة Библиотека шарписта | C#, F#, .NET, ASP.NET (@csharpproglib) في القطاع اللغوي الروسية لاعباً نشطاً. يضم المجتمع حالياً 21 873 مشتركاً، محتلاً المرتبة 6 218 في فئة التكنولوجيات والتطبيقات والمرتبة 30 852 في منطقة روسيا.

📊 مؤشرات الجمهور والحراك

منذ تأسيسه في невідомо، حقق المشروع نمواً سريعاً وجمع 21 873 مشتركاً.

بحسب آخر البيانات بتاريخ 09 يونيو, 2026، تحافظ القناة على نشاط مستقر. خلال آخر 30 يوماً تغيّر عدد الأعضاء بمقدار -88، وفي آخر 24 ساعة بمقدار -7، مع بقاء الوصول العام مرتفعاً.

  • حالة التحقق: غير موثّقة
  • معدل التفاعل (ER): يبلغ متوسط تفاعل الجمهور 11.86‎%. وخلال أول 24 ساعة من النشر يحصد المحتوى عادةً 7.09‎% من ردود الفعل نسبةً إلى إجمالي المشتركين.
  • وصول المنشورات: يحصل كل منشور على متوسط 2 594 مشاهدة. وخلال اليوم الأول يجمع عادةً 1 550 مشاهدة.
  • التفاعلات والاستجابة: يتفاعل الجمهور بانتظام؛ متوسط التفاعلات لكل منشور يبلغ 9.
  • الاهتمامات الموضوعية: يركز المحتوى على مواضيع رئيسية مثل .net, шарписта, навигация, await, string.

📝 الوصف وسياسة المحتوى

يصف المؤلف القناة بأنها مساحة للتعبير عن الآراء الذاتية:
Все самое полезное для C#-разработчика в одном канале. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/b60af5a4 Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead

بفضل وتيرة التحديث المرتفعة (أحدث البيانات بتاريخ 10 يونيو, 2026) تحافظ القناة على حداثتها ومستوى وصول مرتفع. وتُظهر التحليلات تفاعلاً نشطاً من الجمهور، ما يجعلها نقطة تأثير مهمة ضمن فئة التكنولوجيات والتطبيقات.

21 873
المشتركون
-724 ساعات
-187 أيام
-8830 أيام
أرشيف المشاركات
Урок истории До .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: здесь много фриланса, совмещений и серых периодов. ➡️ Куда скрутить накрученный опыт 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта

📍 Навигация: Вакансии • Задачи • Собесы 🐸 Библиотека шарписта #garbage_collector
📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека шарписта #garbage_collector

🌐 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 в мультиагентных системах и не сливать бюджет на токены
В кружке Кирилл рассказал, какие именно подходы будем разбирать. 👉 Занять место на вебинаре