23284
Все самое полезное для C#-разработчика в одном канале. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/b60af5a4 Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead
⚡️ Синхронная коммуникация: HTTP/REST и gRPC
Когда мы говорим о взаимодействии между сервисами, первое, что приходит в голову — синхронный вызов. Один сервис спрашивает, другой отвечает. Просто, понятно, привычно. Разберёмся, какие инструменты есть в .NET и когда каждый из них уместен.
HTTP/REST — разумный дефолт
Большинство проектов начинается именно с REST. Низкий порог входа, понятная отладка через браузер или Postman, огромная экосистема. В .NET для этого используют HttpClient через IHttpClientFactory.
Создание нового HttpClient на каждый запрос — классическая ошибка, которая приводит к исчерпанию сокетов под нагрузкой.
builder.Services.AddHttpClient<IOrderService, OrderService>(client =>
{
client.BaseAddress = new Uri("https://orders-api/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.Timeout = TimeSpan.FromSeconds(30);
});
public class OrderService(HttpClient client) : IOrderService
{
public async Task<Order?> GetAsync(Guid id, CancellationToken ct)
{
var response = await client.GetAsync($"orders/{id}", ct);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<Order>(ct);
}
}
// orders.proto
service Orders {
rpc GetOrder (OrderRequest) returns (OrderReply);
}
message OrderRequest { string id = 1; }
message OrderReply { string id = 1; string status = 2; }
// в сервисе
builder.Services.AddGrpcClient<OrdersClient>(o =>
o.Address = new Uri("https://orders-grpc-service"));
public class OrderHandler(OrdersClient grpc)
{
public async Task<OrderReply> Handle(GetOrderQuery q, CancellationToken ct)
=> await grpc.GetOrderAsync(
new OrderRequest { Id = q.Id.ToString() },
cancellationToken: ct);
}
🆚 Микросервисная архитектура vs Монолит
Монолит — это традиционный подход к разработке, где всё приложение представляет собой единое целое. Весь код, бизнес-логика, UI и доступ к данным находятся в одном проекте и деплоятся как единая единица.
Микросервисы — это архитектурный подход, при котором приложение разбивается на набор небольших, независимых сервисов.
Каждый сервис отвечает за конкретную бизнес-функцию, имеет свою базу данных и может разрабатываться, деплоиться и масштабироваться независимо.
Каждый сервис — это отдельное .NET приложение (мини-монолит) со своей БД, API и бизнес-логикой.
Ключевые отличия
➡️ Структура и организация кода
Монолит:
• Один репозиторий, один проект
• Общие зависимости для всех модулей
• Прямые вызовы методов между компонентами
Микросервисы:
• Множество репозиториев или монорепозиторий
• Независимые зависимости для каждого сервиса
• Коммуникация через API: HTTP, gRPC, сообщения
➡️ Развертывание
Монолит:
• Деплой всего приложения целиком
• Один сервер или кластер серверов
• Простая инфраструктура
Микросервисы:
• Независимый деплой каждого сервиса
• Контейнеризация и оркестрация
• Сложная инфраструктура с API Gateway, Service Discovery
➡️ Масштабирование
Монолит:
• Вертикальное масштабирование — увеличение ресурсов сервера
• Горизонтальное масштабирование всего приложения
• Неэффективно, если нагрузка только на одну функцию
Микросервисы:
• Горизонтальное масштабирование конкретных сервисов
• Масштабируются только те части, которым нужно
• Оптимизация затрат на инфраструктуру
➡️ База данных
Монолит:
• Одна общая база данных
• ACID транзакции работают нативно
• Простота в управлении данными
Микросервисы:
• База данных на каждый сервис
• Распределенные транзакции
• Eventual Consistency
➡️ Технологический стек
Монолит:
• Единый стек технологий
• Сложно внедрить новые технологии
• Один язык программирования
Микросервисы:
• Каждый сервис может использовать свой стек
• Гибкость в выборе технологий
• Можно смешивать .NET, Node.js, Python и др.
Микросервисы — не серебряная пуля, а инструмент для решения конкретных проблем масштаба и сложности.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#il_люминатор
🌐 Forward или Reverse Proxy
Forward и Reverse прокси звучат похоже — оба это прокси. Но работают в противоположных направлениях и решают разные задачи. Давайте разберёмся.
➡️ Forward Proxy
Представьте, что вы хотите отправить письмо, но не хотите раскрывать свой обратный адрес.
Вы отдаёте письмо посреднику. Он отправляет его от своего имени. Получатель видит адрес посредника, а не ваш.
Forward прокси — это посредник между вами и интернетом.
Главное: целевой сервер не знает, что запрос пришёл от вас. Он видит только прокси.
Зачем его используют:
• Логирование всех запросов
• Блокировка нежелательных сайтов
• Экономия трафика через кеширование
• Контроль безопасности
➡️ Reverse Proxy — противоположная история
Теперь представьте большую компанию с множеством отделов.
Когда кто-то звонит в компанию, он попадает на секретаря. Секретарь решает, в какой отдел перенаправить звонок.
Звонящий не знает внутреннюю структуру компании. Он общается только с секретарём.
Reverse прокси — это секретарь для ваших серверов.
Клиент не знает, что за прокси находятся несколько серверов. Он видит только один публичный адрес.
➡️ Реальные примеры использования
1. Балансировка нагрузки
У вас 5 серверов с одним приложением:
Клиент → Reverse Proxy → [Сервер 1]
→ [Сервер 2]
→ [Сервер 3]
→ [Сервер 4]
→ [Сервер 5]
https://mysite.com/api → Сервер на :5000
https://mysite.com/ → Сервер на :3000
https://mysite.com/admin → Сервер на :8080
https://mysite.com/ws → Сервер на :4000
📎 Пропущенный аудит
Метод ExecuteUpdate в EF Core ускоряет массовые обновления, но пропускает перехватчики и события SaveChanges. Это может сломать аудит изменений.
Перехватчик SaveChanges ловит изменения из трекера. Но ExecuteUpdate работает напрямую с БД и игнорирует его:
public override async Task<int> SaveChangesAsync(CancellationToken ct = default)
{
// Захватывает изменения от SaveChanges
var entries = ChangeTracker.Entries()
.Where(e => e.State == EntityState.Modified);
// Но не от ExecuteUpdate!
}
ExecuteUpdate:await context.Products
.Where(p => p.CategoryId == categoryId)
.ExecuteUpdateAsync(s => s
.SetProperty(p => p.Price, newPrice)
.SetProperty(p => p.ModifiedAt, DateTime.UtcNow)
.SetProperty(p => p.ModifiedBy, currentUser));
// Отдельная запись аудита
context.AuditLogs.Add(new AuditLog
{
Action = "BulkPriceUpdate",
EntityType = "Product",
Details = $"Обновил CategoryId={categoryId}, цена={newPrice}"
});
await context.SaveChangesAsync();
⚡️ C# убил Builder для простых случаев
Target-typed new убирает дублирование типов при инициализации, а with-выражения делают иммутабельные обновления одной строкой кода.
Раньше при создании объекта приходилось дважды писать тип: var user = new UserDto { ... }. Теперь достаточно User user = new() { ... }. Компилятор выводит тип из контекста — при возврате из методов, в параметрах, элементах коллекций
Работа с иммутабельными объектами традиционно требовала copy-конструкторов, Builder'а или AutoMapper. with-выражение создаёт поверхностную копию с изменением указанных свойств: var updated = user with { Age = 31 }. Компилятор генерирует код копирования автоматически.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view
👨💻 Span<T> как ref struct
Разбираем вопрос с собеса, в этот раз про Span.Span<T> даёт доступ к памяти без копий и аллокаций. Но почему его сделали ref struct с кучей запретов, и когда лучше взять Memory<T>?
Ответ лежит в нашем канале с вопросами с собесов
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#dotnet_challenge
📂 File-Scoped Namespaces
Cинтаксис file-scoped namespaces заменяет привычное объявление пространства имён со скобками на однострочное с точкой с запятой.
Раньше каждый файл в C# проекте начинался с namespace MyApp.Services { ... }, что добавляло один уровень отступа ко всему коду внутри. Теперь достаточно написать namespace MyApp.Services; в начале файла, и всё содержимое автоматически находится в этом пространстве имён без дополнительной вложенности.
Код становится более плоским. Вместо того чтобы начинать каждый класс с двух уровней отступа: namespace + class, вы сразу работаете с одним.
При рефакторинге, когда нужно переместить класс в другой namespace, старый подход требовал изменения строки с объявлением. В git diff это выглядело как замена всего файла, даже если логика класса не менялась. C новым подходом меняется только одна строка вверху.
Компилятор не видит разницы — это чисто синтаксический сахар. IL-код идентичен, производительность не меняется.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view
🌼 Бустим рабочее место за 10 шагов
Удаленка — это не только про свободу выбора места работы, но и про умение создать условия для продуктивности.
➡️ В карточках 5 советов по обустройству рабочего места, а остальные в статье.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
⚡️ Почему IQueryable не должен покидать слой данных
Разработчики используют EF Core и передают IQueryable по всему приложению, считая это эффективным подходом. Но такая практика создаёт серьёзные проблемы с архитектурой и сопровождением кода.IQueryable позволяет строить динамические SQL-запросы, выполняя фильтрацию на стороне БД, а не в памяти приложения:
var startDate = new DateTime(2023, 1, 1);
// Плохо: загружаем всё в память, затем фильтруем
var allRecords = context.Purchases.ToList();
var filtered = allRecords.Where(p => p.Date > startDate).ToList();
// Хорошо: фильтрация выполняется базой данных
var filtered = context.Purchases
.Where(p => p.Date > startDate)
.ToList();
IQueryable начинает путешествовать через слои приложения, возникают проблемы.// Репозиторий
public class PurchaseRepository
{
public IQueryable<Purchase> GetAll() =>
_context.Purchases.AsQueryable();
}
// Сервис бизнес-логики
public class PurchaseService
{
public IQueryable<Purchase> GetActive() =>
_repository.GetAll().Where(p => p.Status == "Active");
}
// Контроллер
var userPurchases = _service.GetActive()
.Where(p => p.UserId == currentUserId);
// Представление
@foreach(var item in Model.Where(p => p.IsCompleted))
{
<!-- рендеринг -->
}
IQueryable наследует IEnumerable — код может не знать, что работает с запросом к БДpublic interface IPurchaseRepository
{
Task<List<Purchase>> FindAsync(Expression<Func<Purchase, bool>> predicate);
}
// Использование
var purchases = await _repository.FindAsync(
p => p.IsCompleted && p.UserId == currentUserId);
🧑💻 Закон Хайрама: почему вы не можете изменить свой API
Формулировка закона:
При достаточном количестве пользователей API не имеет значения, что вы обещаете в контракте — все наблюдаемое поведение вашей системы станет чьей-то зависимостью.
👀 Смотрим видео про URL
Работайте с API каждый день, но часто ли задумываетесь, как правильно строить эндпоинты.
В видео подробно про пагинацию, query-параметры, фильтры, сортировки, поддомены, порты и пути с версионированием.
➡️ Подтянуть базу про URL
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#il_люминатор
🟥 Утечки данных в .NET-приложениях чаще происходят не из-за хакеров, а из-за неверной работы с ключами, конфигурациями и базовой криптографией. Ошибки на уровне кода быстро превращаются в инциденты.
На открытом уроке разберём практическое применение System.Security.Cryptography: AES, RSA, хеширование и цифровые подписи. Поговорим о безопасном хранении и передаче секретов, управлении ключами и принципе наименьших привилегий.
❗️ Вы системно разберёте уязвимости OWASP Top-10 для .NET: инъекции, небезопасную десериализацию, XSS, CSRF — и способы их нейтрализации на уровне кода и архитектуры. Отдельно обсудим шифрование данных в БД, защиту строк подключения и безопасное логирование без утечек токенов.
🟥Встречаемся 10 февраля в 20:00 МСК в преддверии старта курса «C# Developer. Professional».
Зарегистрируйтесь, чтобы не пропустить: https://clc.to/UzMt9g
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
📎 Один файл превращается в полноценное приложение
В .NET 10 появилась фича, которая позволяет запускать C# код прямо из одного .cs файла без создания проекта.
Помимо этого туда можно подключать NuGet пакеты. Директива package в помощь:
#:package Newtonsoft.Json
using Newtonsoft.Json;
string json = "{\"name\": \"Иван\", \"age\": 30}";
var obj = JsonConvert.DeserializeObject<dynamic>(json);
Console.WriteLine($"{obj.name}, {obj.age} лет");
#:package Newtonsoft.Json@13.0.3
AI в экосистеме .NET: оставайтесь в тренде
Microsoft глубоко интегрировала AI (Semantic Kernel, Copilot) в нашу среду, но принципы работы агентов универсальны. Понимание того, как строятся цепочки ReAct или RAG, необходимо любому сеньору в 2026 году.
На курсе мы даём фундаментальную базу и практику (на Python-стеке, стандарте AI-индустрии). Эти знания позволят вам эффективно использовать AI-возможности в C
или создавать гибридные системы.
Архитектурный фокус:
— Оркестрация агентов (принципы применимы и к SK).
— Работа с векторными данными в SQL/NoSQL.
— Проектирование Stateful-систем с памятью.
Погрузиться в архитектуру AI
☕️ Веб-приложение для продуктивности
Разработчик запустил в продакшн своё веб-приложение для управления временем — The Coffee Timer. Проект реализует технику Pomodoro с кофейной эстетикой.
Приложение предлагает классический таймер Pomodoro с 25-минутными рабочими сессиями и визуализацией в виде анимированной кофейной чашки.
Бэк построен на .NET, фронт —React + TypeScript + Tailwind CSS. В качестве базы данных используется Supabase.
Хороший пример современного full-stack проекта с использованием .NET.
➡️ Попробовать приложение
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#sharp_view
💿 S.M.A.R.T. уже знает, что с вашим диском
Диск умирает тихо. Сначала один переназначенный сектор, потом десять, потом в один прекрасный день файлы просто не открываются. Хорошо, если это была музыка. Плохо, если рабочие документы за три года.
Как давно вы проверяли свои файловые хранилища?
Как раз вышла новая версия CrystalDiskInfo. Ничего революционного — но повод скачать актуальную сборку и наконец запустить её.
➡️ Проверить диск
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
😊 Менторство на работе
Вас просят поменторить нового разработчика. Давайте разберемся, когда это возможность, а когда — скрытая эксплуатация.
Идеальная ситуация
Компания доплачивает 15-20% к зарплате, джун быстро схватывает и задает умные вопросы. Вы получаете деньги, прокачиваете скилл объяснения. Все в плюсе.
Ловушка «помоги новенькому»
«Покажи, как тут все устроено» может быть и без доплаты, без официального оформления. Вы делаете две работы за одну зарплату. Времени уходит 5-10 часов в неделю, дедлайны остаются прежними. Через месяц выгорание и раздражение на джуна, хотя он не виноват.
Выход: либо официальное оформление с корректировкой нагрузки, либо вежливое «сейчас не могу взять это на себя».
Человек не понимает базовых вещей после третьего объяснения, не гуглит, не пытается разобраться сам. Вы превращаетесь в костыль, каждый вопрос съедает время, а прогресса нет. Проблема часто не в способностях, а в мотивации или ошибке на этапе найма.
Честный разговор с лидом — это нормально. Менторство работает при наличии базы и желания учиться. Если база нулевая, это провал рекрутинга, не ваша ответственность.
💬 Менторили джунов? Как у вас это происходило? Делитесь в комментах 👇
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#entry_point
🔄 Дайджест недели
Коротко про события недели.
— Microsoft разрабатывает функцию перемещения панели задач в Windows 11
Функция ожидается летом 2026 года как часть усилий по улучшению репутации ОС.
— Linux 6.19
— Превью .NET 11
— Бустим рабочее место за 10 шагов
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#garbage_collector
Собираем фулл-хаус: 3 курса по цене 1
Хватит выбирать между «полезно», «модно» и «для души». Мы запустили механику, которая позволяет собрать кастомный стек навыков без удара по бюджету: покупаете один курс — два других забираете бесплатно.
В Enterprise ценится фундаментальность. Углубитесь в архитектуру и шаблоны проектирования для построения сложных систем или подтяните алгоритмы и структуры данных для технических интервью.
Для тех, кто хочет понимать математику ML-моделей (вне зависимости от языка) — полный набор по AI: от линала и ML-старта до разработки автономных агентов.
Ну и классика: обновлённый Python как второй язык для скриптов.
Собрать свой пак
🧑💻 Когда чистый код всё равно требует комментариев
Чистый код делает программу понятной без лишних объяснений. Но иногда комментарии необходимы, чтобы передать суть решений, которые не уместить в самих строках.
Роберт Мартин в книге «Чистый код» не запрещает комментарии полностью. Он против тех, что дублируют очевидное или устаревают со временем.
Когда комментарии спасают ситуацию
• Сложная логика или хак:
// Обход бага в IE11 с обработкой форм
if (isIE11()) {
polyfillFormSubmit();
}
// Не трогать: изменение сломает кэш в проде до деплоя новой версии
cache.invalidateOnlyOnRestart();
🧑💻 Композиция функций в C#
Функциональное программирование — не просто академическая теория. Это парадигма, превращающая императивный спагетти-код в декларативные, тестируемые и поддерживаемые решения.
Проблема императивного кода:
// Традиционный подход - может быть трудно читать и сложно тестировать
public OrderDto ProcessOrder(int orderId)
{
var order = _dbContext.Orders.Find(orderId);
if (order == null) throw new NotFoundException();
if (order.Status != "Pending")
throw new InvalidOperationException();
var customer = _dbContext.Customers.Find(order.CustomerId);
if (!customer.IsActive)
throw new InvalidOperationException();
var discount = customer.IsVip ? 0.1m :
order.Total > 1000 ? 0.05m : 0m;
order.FinalTotal = order.Total * (1 - discount);
_dbContext.SaveChanges();
return new OrderDto { ... };
}
public class OrderProcessingPipeline
{
private readonly Func<int, Task<Order?>> _loadOrder;
private readonly Func<Order, Task<Customer?>> _loadCustomer;
public async Task<Result<OrderDto, Error>> ProcessAsync(int orderId)
{
return await Result<Order, Error>
.FromNullable(await _loadOrder(orderId), Error.OrderNotFound)
.BindAsync(_loadCustomer)
.BindAsync(_validateStatus)
.BindAsync(_calculateDiscount)
.BindAsync(_persistOrder)
.MapAsync(_mapToDto);
}
}
public static class PricingFunctions
{
public static decimal CalculateDiscount(CustomerType type, decimal total) =>
type switch
{
CustomerType.Vip => total * 0.15m,
CustomerType.Premium => total * 0.10m,
_ => 0m
};
public static decimal ApplyTax(decimal amount, decimal rate) =>
amount * (1 + rate);
// Композиция
public static Func<CustomerType, decimal, decimal> CalculateFinalPrice =>
(type, total) => ApplyTax(total - CalculateDiscount(type, total), 0.08m);
}
[Theory]
[InlineData(CustomerType.Vip, 1000, 918.00)]
public void CalculateFinalPrice_ReturnsExpected(CustomerType type, decimal total, decimal expected)
{
Assert.Equal(expected, PricingFunctions.CalculateFinalPrice(type, total));
}
📷 Подборка вакансий для шарпистов
C#/.NET разработчик — 265 000 ₽. Удалёнка
Middle/Senior .NET Developer — от 3 000 до 3 500 $. Удалёнка
C#/.NET-разработчик — Удалёнка
➡️ Еще больше топовых вакансий — в нашем канале C# Jobs
🐸 Библиотека шарписта
🤩 Свежее превью .NET
Microsoft выпустила первую предварительную версию .NET 11 с улучшениями во всех областях: рантайм, SDK, библиотеки, C#, ASP.NET Core, Blazor и .NET MAUI.
➡️ Библиотеки
• Поддержка Zstandard compression для эффективного сжатия
• BFloat16 — новый тип с плавающей точкой для ML-сценариев
• Улучшения ZipArchiveEntry для работы с архивами
• FrozenDictionary теперь поддерживает collection expressions
➡️ API и утилиты:
• Расширенная поддержка Rune в String, StringBuilder и TextWriter
• MediaTypeMap для определения MIME-типов
• API верификации HMAC и KMAC
• Создание жёстких ссылок
• DivisionRounding для режимов целочисленного деления
• Happy Eyeballs в Socket.ConnectAsync для быстрого подключения по IPv4/IPv6
➡️ Рантайм
• Runtime Async — улучшенная асинхронность на уровне рантайма
• CoreCLR на WebAssembly — полноценный рантайм в браузере
• Расширение интерпретатора
• JIT-оптимизации производительности
• GC Heap Hard Limit для 32-битных процессов
• Поддержка архитектур RISC-V и s390x
➡️ SDK
• dotnet run теперь интерактивно выбирает целевой фреймворк и устройство
• dotnet test поддерживает позиционные аргументы
• dotnet watch — горячая перезагрузка для изменений в ссылках и настраиваемые порты
➡️ C# и F#
• C#: передача коллекций как аргументов
• C# расширенная поддержка раскладки памяти
• F#: параллельная компиляция включена по умолчанию, ускорение компиляции вычислительных выражений, новые флаги --disableLanguageFeature и --typecheck-only для FSI.
➡️ Блог разработчиков
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
⭐️ SQL Server в одном файле
LocalDB — это SQL Server в одном .mdf файле. Идеально для тестов, локальной разработки и инструментов. Создаётся одной командой, не требует установки сервера.
LocalDB — это облегчённая версия SQL Server, которая: работает в режиме пользователя, то есть без сервиса, хранит базу в одном .mdf файле, ведёт себя как настоящий SQL Server и не требует установки полноценного SQL Server.
Минимальный рабочий пример:
using Microsoft.Data.SqlClient;
using System.IO;
const string DatabaseName = "TestDatabase";
var databasePath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"MyApp", "Data"
);
// Создаём директорию если её нет
if (!Directory.Exists(databasePath))
{
Directory.CreateDirectory(databasePath);
}
var dataFile = Path.Combine(databasePath, $"{DatabaseName}.mdf");
var logFile = Path.Combine(databasePath, $"{DatabaseName}_log.ldf");
// Безопасное экранирование для SQL
var escapedDbName = DatabaseName.Replace("]", "]]");
var escapedDataPath = dataFile.Replace("'", "''");
var escapedLogPath = logFile.Replace("'", "''");
var masterConnection = new SqlConnectionStringBuilder
{
DataSource = @"(localdb)\MSSQLLocalDB",
InitialCatalog = "master",
IntegratedSecurity = true,
TrustServerCertificate = true
}.ConnectionString;
var createDbQuery = $"""
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = N'{escapedDbName}')
BEGIN
CREATE DATABASE [{escapedDbName}]
ON PRIMARY (
NAME = N'{escapedDbName}',
FILENAME = N'{escapedDataPath}'
)
LOG ON (
NAME = N'{escapedDbName}_log',
FILENAME = N'{escapedLogPath}'
)
COLLATE Latin1_General_100_CI_AI_SC_UTF8;
END
""";
using var conn = new SqlConnection(masterConnection);
await conn.OpenAsync();
using var cmd = new SqlCommand(createDbQuery, conn);
await cmd.ExecuteNonQueryAsync();
Console.WriteLine($"База данных создана: {dataFile}");
👍 Модификатор protected internal
В C# модификатор доступа protected internal открывает доступ к полю, методу или свойству из двух зон. Любые классы внутри той же сборки могут обращаться к такому члену напрямую.
Наследники базового класса получают доступ, даже если они в другой сборке.
Пример:
public class BaseClass
{
protected internal int SecretValue = 42;
}
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#garbage_collector
📰 Дайджест недели
Уже прошла первая полноценная неделя февраля, собрали лучшее в один пост.
— Git v2.53
— Фриланс для разработчиков
— Миграция с GitHub
— Работа с LLM в .NET
— Веб-приложение для продуктивности
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#async_news
🧑💻 Забытый await: от race condition до DoS-атаки
В C# ключевое слово async — это инструкция компилятору превратить метод в state machine. await отмечает точку логической приостановки. Всё просто: выполнение не должно продолжаться, пока результат не существует.
При await весь код выполняется синхронно, пока не достигнет I/O или функции, возвращающей Task. Если Task не завершён, происходит раскрутка кадра стека, и поскольку обрамляющие методы приостановлены до самого верха, поток может быть освобождён в пул потоков.
Позже задача завершается, продолжение планируется и выполнение возобновляется там, где остановилось. Это не параллельное выполнение, это приостановка и возобновление.
Если забыли await:
var task = GetDataAsync();
// забыли await
Use(task);
await может привести к классическому состоянию гонки: код продолжает выполнение до завершения критической операции, например, проверки прав доступа, открывая окно для эксплуатации. async/await приводит к истощению пула потоков. Злоумышленник может намеренно генерировать запросы, эксплуатирующие блокирующие операции.
📍 Навигация: Вакансии • Задачи • Собесы
🐸Библиотека шарписта
#garbage_collector