Библиотека шарписта | C#, F#, .NET, ASP.NET
22K subscribers
2.76K photos
41 videos
85 files
5.14K links
Все самое полезное для C#-разработчика в одном канале.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/b60af5a4

Для обратной связи: @proglibrary_feeedback_bot

РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead
Download Telegram
🧑‍💻 Больше не нужно открывать терминал от администратора

На Linux и macOS всё просто: нужны права администратора, добавили sudo перед командой. На Windows для этого приходилось открывать новый терминал от имени администратора, делать правый клик, подтверждать UAC. Microsoft наконец добавила sudo в Windows 11 нативно, начиная со сборки 26045.

Что это такое

Sudo for Windows позволяет запускать команды с правами администратора прямо из обычного терминала, без переоткрытия сессии. Это не порт Unix-утилиты, а отдельная Windows-реализация той же идеи. Некоторые детали поведения отличаются от привычного sudo на Linux.

Как включить

Через настройки: Параметры → Система → Для разработчиков → Включить sudo.

Или через командную строку с правами администратора:
sudo config --enable normal


Три режима работы

forceNewWindow — режим по умолчанию. Команда запускается в новом окне с правами администратора. Похоже на runas, но удобнее.

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

normal — полный инлайн-режим, как на Linux. Команда запускается в текущем окне и может принимать ввод. Наиболее удобный вариант, но несёт больше рисков безопасности.

Переключение между режимами:
sudo config --enable forceNewWindow
sudo config --enable disableInput
sudo config --enable normal


Примеры использования

Редактирование системного файла:
sudo notepad C:\Windows\System32\drivers\etc\hosts


Установка глобального npm-пакета:
sudo npm install -g package-name


Проверка сетевых соединений:
sudo netstat -ab


Про безопасность

При запуске через sudo появляется UAC-запрос на подтверждение. Режимы disableInput и normal несут дополнительные риски: нежелательный процесс с низкими правами теоретически может взаимодействовать с повышенным процессом. Для большинства сценариев рекомендован forceNewWindow.

Что нужно учесть

Инструмент доступен только на Windows 11, начиная с версии 24H2. Управлять им можно через настройки, командную строку или реестр (HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Sudo). Есть и GPO-политика для корпоративного управления.

➡️ Репозиторий

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍62😁1
💡 Фильтрация коллекции по типу

Когда в коллекции хранятся объекты разных типов, обычный перебор с ручной проверкой is или as быстро превращается в шаблонный код. OfType<T> решает это в одну строку.

Пример:
var mixedList = new List<object> { 1, "hello", 2.5, "world", 3, true };

var stringsOnly = mixedList.OfType<string>();

// Результат: hello, world


Из шести элементов метод выбирает только строки. Числа, булево значение и double игнорируются.

Как это работает под капотом

OfType<T> использует оператор is для каждого элемента. Примерно так выглядит его реализация:
public static IEnumerable<T> OfType<T>(this IEnumerable source)
{
foreach (var item in source)
{
if (item is T typed)
yield return typed;
}
}


Метод ленивый: перебор происходит только при итерации по результату, не сразу при вызове.

Когда использовать

Удобно при работе с IEnumerable<object>, коллекциями ArrayList из старого кода, или когда нужно вытащить конкретный тип из гетерогенной иерархии объектов.

var textBoxes = Controls.OfType<TextBox>();


Чем отличается от Cast<T>

Cast<T> бросает InvalidCastException, если встречает несовместимый тип. OfType<T> просто пропускает такой элемент. Если коллекция точно однородная и ошибка должна сигнализировать о проблеме, уместен Cast<T>. В остальных случаях безопаснее OfType<T>.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍135😁1
🗺 Группировка меток в картах MAUI

Если на карте десятки или сотни меток, они сливаются в непроходимую кашу. В .NET MAUI 11 Preview 3 появилась кластеризация меток из коробки на Android и iOS/Mac Catalyst.

Как включить

Одно свойство:
<maps:Map IsClusteringEnabled="True" />


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

Разные группы кластеров

Если нужно, чтобы кофейни кластеризовались отдельно от парков, используем ClusteringIdentifier на Pin:
map.Pins.Add(new Pin
{
Label = "Pike Place Coffee",
Location = new Location(47.6097, -122.3331),
ClusteringIdentifier = "coffee"
});

map.Pins.Add(new Pin
{
Label = "Occidental Square",
Location = new Location(47.6064, -122.3325),
ClusteringIdentifier = "parks"
});


Пины с одинаковым идентификатором группируются вместе. С разными — образуют независимые кластеры, даже если географически рядом. Пины без идентификатора попадают в общую группу по умолчанию.

Обработка нажатия на кластер

При тапе на кластер срабатывает событие ClusterClicked:
map.ClusterClicked += async (sender, e) =>
{
string names = string.Join("\n", e.Pins.Select(p => p.Label));
await DisplayAlert(
$"Кластер ({e.Pins.Count} меток)",
names,
"OK");

// Отменить стандартное приближение к кластеру:
// e.Handled = true;
};


В ClusterClickedEventArgs три поля:

Pins — список пинов в кластере
Location — географический центр
Handled — если true, стандартное поведение (зум к кластеру) отменяется.

Платформенные детали

На Android кластеризация работает через собственный алгоритм на основе сетки, пересчитывается при смене масштаба. Сторонние зависимости не нужны.

На iOS и Mac Catalyst используется нативный MKClusterAnnotation из MapKit — анимации и поведение платформенные.

Пример с кластеризацией уже есть в репозитории, страница Clustering в проекте MapDemo.

➡️ Блог разработчиков

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍2😁1
🧑‍💻 ИИ найдёт слабые места в вашем резюме

Лайфхак, который используют единицы: найти профили людей с нужным тайтлом, которые уже устроились и проанализировать их через ИИ.

200–300 профилей дадут чёткую картину: в чём вы сильнее рынка, где сливаете на фоне конкурентов и что исправить в резюме прямо сейчас.

➡️ Это лишь один из 5 промптов из статьи

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта
Please open Telegram to view this post
VIEW IN TELEGRAM
😁1
This media is not supported in your browser
VIEW IN TELEGRAM
🗓 В следующий вторник (28.04) в 19:00 встречаемся в онлайне.

Тема:
Как эффективно управлять контекстным окном LLM в мультиагентных системах и не сливать бюджет на токены


В кружке Кирилл рассказал, какие именно подходы будем разбирать.

👉 Занять место на вебинаре
Please open Telegram to view this post
VIEW IN TELEGRAM
😁21
❗️ Внеплановое обновление .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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍145😁3
🧑‍💻 Подборка вакансий для шарпистов

Старший Backend C# ASP. NET разработчик — от 300 000 до 400 000 ₽ , удалёнка или гибрид в Москве

Разработчик C# — офис в Минске

Unity Developer — удалёнка

➡️ Еще больше топовых вакансий — в нашем канале C# Jobs

🐸 Библиотека шарписта
Please open Telegram to view this post
VIEW IN TELEGRAM
3😁1
🛠 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 — стабильный 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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5😁1
⚙️ Сервер тормозит без видимых причин

Представьте картину: CPU загружен на 30–40%, ошибок нет, но запросы внезапно начинают тормозить, а время ответа под нагрузкой улетает в небо. Это не магия и не баг в инфраструктуре. Скорее всего, вы столкнулись с голоданием пула потоков.

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

Причина почти всегда в одном из таких паттернов:
var data = httpClient.GetStringAsync(url).Result; // блокирует поток

Task.Run(() => DoWork()).Wait(); // форсированная синхронизация


Эти вызовы берут асинхронную операцию и намеренно блокируют поток до её завершения. В результате поток занят, но не делает ничего полезного, а просто ждёт.

Попробуйте сделать так и замеряйте результат:
var data = await httpClient.GetStringAsync(url); 


📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱83👍2😁1🤔1
🤩 Валидация для .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-паттернах.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍3
👋 Небольшой вопрос для прокачки

Это классика технических интервью в C#:
Статический конструктор — когда именно его вызывает рантайм


Вы не пишете new MyClass(), не обращаетесь к объекту, не делаете вообще ничего явного. А он всё равно срабатывает.

➡️ Узнайте ответ

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#dotnet_challenge
Please open Telegram to view this post
VIEW IN TELEGRAM
7😁1
🛠 Структуры не всегда быстрее

Распространённое заблуждение среди разработчиков на 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_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍42😁2
🤩 .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
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥81😁1
🛠 От горизонтальных слоёв к вертикальным срезам

Структура 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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🤔3😁21😢1
🔄 Мелкий релиз с нужными правками 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
Please open Telegram to view this post
VIEW IN TELEGRAM
1😁1
🔥 База по ИИ-агентам от научного сотрудника Сколтеха и НИУ ВШЭ

Знакомьтесь, Екатерина Трофимова. Кандидат компьютерных наук, ресерчер в Центре ИИ Сколтеха и лаборатории 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 и начать обучение
3😁1
📰 Любимый автор по C#

Кого смотрите и читаете по C# и .NET? Авторы на YouTube, Хабре, телеграм-каналы, подкасты — пишите в комменты 💬

Мы, естественно, следим за Ником Чапсасом, но может есть менее гигантские медиа-личности?

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#entry_point
Please open Telegram to view this post
VIEW IN TELEGRAM
😁3❤‍🔥1