Современный код на .NET редко выполняется изолированно.
Тревожный сигнал:
static List<Order> _orders = new();
List<T> не потокобезопасен — параллельная запись приведёт к повреждению данных или исключению.Лучше:
static ConcurrentBag<Order> _orders = new();
Но ещё лучше избавиться от глобального изменяемого состояния полностью. Статические поля — это скрытые зависимости, которые делают код менее тестируемым и непредсказуемым под нагрузкой.
Вопросы для ревью такого кода:
— Защищено ли общее состояние?
— Есть ли состояния гонки?
— Безопасен ли код при параллельном выполнении?
— Действительно ли асинхронные методы потокобезопасны?
— Нет ли скрытого изменяемого состояния в статических полях?
— Используются ли потокобезопасные коллекции там, где это нужно?
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱6❤2
Сделали сниппет кода на манер JavaScript, складываем число и строку. В C#, в отличие от JS, это операция предсказуемая и можно догадаться о результате.
📍 Навигация: Вакансии • Задачи • Собесы
#dotnet_challenge
Please open Telegram to view this post
VIEW IN TELEGRAM
Последний шанс: 3 курса по цене 1 и запуск AI-агентов в продакшн
Дёргать API OpenAI из
В обновлённой программе фокус смещён на жёсткий инжиниринг и вывод мультиагентных систем в прод. Вы научитесь строить ReAct-циклы, работать с оркестраторами вроде
Почему нельзя откладывать:
— масштабная акция «3 курса по цене 1» сгорит уже завтра;
— промокод
— сразу после оформления открываются материалы для подготовки — начать учиться можно прямо сейчас.
Забронировать место на курсе и забрать бонусы до 28 февраля
Дёргать API OpenAI из
.NET-сервисов — весело, но энтерпрайзу нужна предсказуемость. Как интегрировать ИИ-агентов в корпоративную инфраструктуру, контролировать затраты и не нарушить 152-ФЗ?В обновлённой программе фокус смещён на жёсткий инжиниринг и вывод мультиагентных систем в прод. Вы научитесь строить ReAct-циклы, работать с оркестраторами вроде
LangGraph, внедрять продвинутый RAG, протоколы MCP и AgentOps. Все ключевые навыки в одном месте: измеримость систем, интеграция с легаси, human-in-the-loop и развёртывание LLM-решений в закрытых контурах.Почему нельзя откладывать:
— масштабная акция «3 курса по цене 1» сгорит уже завтра;
— промокод
Agent на скидку 10 000 рублей действует последние часы;— сразу после оформления открываются материалы для подготовки — начать учиться можно прямо сейчас.
Забронировать место на курсе и забрать бонусы до 28 февраля
string — это ссылочный тип с особой поддержкой рантайма. Внутри он хранит непрерывный блок UTF-16 кодовых единиц.
Отсюда первый сюрприз:
string s = "😊";
Console.WriteLine(s.Length); // 2, не 1
Length считает не символы, а UTF-16 кодовые единицы. Эмодзи и многие Unicode-символы занимают две единицы — суррогатную пару.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16❤5🤔1
🌍 Сравнение строк и культура
Когда вы сравниваете строки в C#, результат зависит не только от содержимого, но и от того, как вы сравниваете. И здесь кроется целый класс багов, которые воспроизводятся только на машинах с определёнными региональными настройками.
Два режима сравнения:
Пример с немецким языком:
Один и тот же код даёт разные результаты в зависимости от того, где запущен. На машине разработчика работает, на продакшен-сервере — нет.
Турецкая проблема:
Именно из-за этого Microsoft SQL Server годами имел баги в коде, который работал везде, кроме турецких инсталляций.
Анализатор CA1307 в .NET предупреждает о вызовах сравнения без явного указания StringComparison. Включите его — он поймает большинство таких мест автоматически.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#il_люминатор
Когда вы сравниваете строки в C#, результат зависит не только от содержимого, но и от того, как вы сравниваете. И здесь кроется целый класс багов, которые воспроизводятся только на машинах с определёнными региональными настройками.
Два режима сравнения:
Ordinal — побайтовое сравнение Unicode-значений. Быстрое, предсказуемое, не зависит от культуры.CurrentCulture — сравнение по правилам текущей локали операционной системы. Медленнее, результат может отличаться на разных машинах.Пример с немецким языком:
string a = "straße"; // немецкое "улица"
string b = "strasse"; // то же слово, другое написание
a.Equals(b, StringComparison.CurrentCultureIgnoreCase); // true на de-DE
a.Equals(b, StringComparison.CurrentCultureIgnoreCase); // false на en-US
a.Equals(b, StringComparison.OrdinalIgnoreCase); // false везде
Один и тот же код даёт разные результаты в зависимости от того, где запущен. На машине разработчика работает, на продакшен-сервере — нет.
Турецкая проблема:
// В турецкой локали (tr-TR):
"I".ToLower() == "ı" // не "i", а "ı" (без точки)
"i".ToUpper() == "İ" // не "I", а "İ" (с точкой)
// Поэтому:
string input = "FILE";
input.ToLower() == "file" // false на турецкой машине!
Именно из-за этого Microsoft SQL Server годами имел баги в коде, который работал везде, кроме турецких инсталляций.
Анализатор CA1307 в .NET предупреждает о вызовах сравнения без явного указания StringComparison. Включите его — он поймает большинство таких мест автоматически.
📍 Навигация: Вакансии • Задачи • Собесы
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🤔5❤3
📰 Дайджест недели
Один пост — лучшее недели.
— TDD в больших проектах
— GitHub Copilot Testing в Visual Studio 2026
— Как выбрать render mode в Blazor
— PVS-Studio 7.41
— Один интерфейс — любой канал для уведомлений
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
Один пост — лучшее недели.
— TDD в больших проектах
— GitHub Copilot Testing в Visual Studio 2026
— Как выбрать render mode в Blazor
— PVS-Studio 7.41
— Один интерфейс — любой канал для уведомлений
📍 Навигация: Вакансии • Задачи • Собесы
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
Энтерпрайз убивает драйв? Стань техническим кофаундером в EdTech-стартапе 🚀
Proglib App — платформа для обучения разработчиков (курсы, квизы, ИИ-агенты). MVP уже в проде, пользователи растут. Проекту нужен сильный инженер, готовый взять на себя роль технического лидера и строить продукт вместе с основателем.
Это вызов для тех, кто перерос роль «просто разработчика» и хочет влиять на архитектуру и бизнес-логику напрямую.
🛠️ Стек (современный Fullstack):
TypeScript, React 18, Express 5, PostgreSQL, Drizzle ORM.
Почему это может быть интересно:
• Никакой бюрократии: только код, продукт и архитектурные решения.
• Инструменты: работа с ИИ-ассистентами (Claude Code, Cursor).
• Рост: путь от MVP до масштабного сервиса в роли партнёра.
Ожидания:
• Крепкий бэкграунд в разработке и понимание архитектуры БД.
• Готовность работать с TS/React/Node.js.
• Автономность и продуктовое мышление.
Удалёнка, гибкий график, полная свобода в реализации идей.
Готов сменить привычный стек на роль кофаундера? Пиши о себе и кидай GitHub 👇
@proglibrary_feedback_bot
Proglib App — платформа для обучения разработчиков (курсы, квизы, ИИ-агенты). MVP уже в проде, пользователи растут. Проекту нужен сильный инженер, готовый взять на себя роль технического лидера и строить продукт вместе с основателем.
Это вызов для тех, кто перерос роль «просто разработчика» и хочет влиять на архитектуру и бизнес-логику напрямую.
🛠️ Стек (современный Fullstack):
TypeScript, React 18, Express 5, PostgreSQL, Drizzle ORM.
Почему это может быть интересно:
• Никакой бюрократии: только код, продукт и архитектурные решения.
• Инструменты: работа с ИИ-ассистентами (Claude Code, Cursor).
• Рост: путь от MVP до масштабного сервиса в роли партнёра.
Ожидания:
• Крепкий бэкграунд в разработке и понимание архитектуры БД.
• Готовность работать с TS/React/Node.js.
• Автономность и продуктовое мышление.
Удалёнка, гибкий график, полная свобода в реализации идей.
Готов сменить привычный стек на роль кофаундера? Пиши о себе и кидай GitHub 👇
@proglibrary_feedback_bot
❤2👍1
📝 Логирование со строковой интерполяцией
Логирование c интерполяцией строк один из самых распространённых источников лишних аллокаций в .NET-приложениях.
Особенность в том, что код выглядит абсолютно нормально и работает правильно — просто дорого.
Что происходит с интерполяцией:
Компилятор разворачивает это примерно в:
Строка формируется до вызова метода. Если уровень логирования
Структурное логирование решает проблему:
Здесь строка — это шаблон. Аргументы передаются отдельно. Логгер сначала проверяет, активен ли уровень, и только потом форматирует сообщение. Если уровень выключен — аллокации нет вообще.
Бонус: структурное логирование позволяет индексировать поля в системах вроде Seq, Elasticsearch, Datadog — вы сможете искать по UserId как по полю, а не парсить текст.
Начиная с .NET 6 есть ещё лучший вариант через compile-time source generators:
Никаких аллокаций, никакого боксинга, максимальная производительность — и всё это без изменения читаемости кода.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#il_люминатор
Логирование c интерполяцией строк один из самых распространённых источников лишних аллокаций в .NET-приложениях.
Особенность в том, что код выглядит абсолютно нормально и работает правильно — просто дорого.
Что происходит с интерполяцией:
logger.LogInformation($"User {userId} logged in at {time}");Компилятор разворачивает это примерно в:
logger.LogInformation(string.Format("User {0} logged in at {1}", userId, time));Строка формируется до вызова метода. Если уровень логирования
Information отключён, строка всё равно создаётся, занимает память и тут же выбрасывается сборщиком мусора.Структурное логирование решает проблему:
logger.LogInformation("User {UserId} logged in at {Time}", userId, time);Здесь строка — это шаблон. Аргументы передаются отдельно. Логгер сначала проверяет, активен ли уровень, и только потом форматирует сообщение. Если уровень выключен — аллокации нет вообще.
Бонус: структурное логирование позволяет индексировать поля в системах вроде Seq, Elasticsearch, Datadog — вы сможете искать по UserId как по полю, а не парсить текст.
Начиная с .NET 6 есть ещё лучший вариант через compile-time source generators:
// Генерирует оптимальный код на этапе компиляции:
[LoggerMessage(Level = LogLevel.Information, Message = "User {UserId} logged in at {Time}")]
partial void LogUserLogin(int userId, DateTime time);
Никаких аллокаций, никакого боксинга, максимальная производительность — и всё это без изменения читаемости кода.
📍 Навигация: Вакансии • Задачи • Собесы
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21❤6🤔1
🔄 Cake 6.0.0 вышел
Cake — это инструмент автоматизации сборки для .NET-проектов. Скрипты пишутся на C# с простым DSL, который покрывает компиляцию, тестирование, публикацию и всё, что обычно делают в CI/CD пайплайне.
Логирование теперь принимает интерполированные строки напрямую. Сообщение формируется только если текущий уровень логирования активен:
Cake.Sdk получил поддержку in-process NuGet клиента. Теперь через
Добавлена поддержка
Из других изменений: NuGetPack теперь поддерживает поле ReadMe для readme пакета, в AssemblyInfo creator можно добавлять несколько экземпляров одного атрибута, in-process NuGet restore заработал с аутентификацией через приватные фиды.
Есть breaking change для GitLab CI — поля CI_PIPELINE_ID и CI_BUILD_ID сменили тип с int на long, так как на gitlab.com идентификаторы давно вышли за пределы 32 бит.
➡️ Release Notes
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
Cake — это инструмент автоматизации сборки для .NET-проектов. Скрипты пишутся на C# с простым DSL, который покрывает компиляцию, тестирование, публикацию и всё, что обычно делают в CI/CD пайплайне.
Логирование теперь принимает интерполированные строки напрямую. Сообщение формируется только если текущий уровень логирования активен:
Information($"Building {project} v{version}");
Verbose(log => log($"Processing {items.Count} items in {stopwatch.ElapsedMilliseconds} ms"));Cake.Sdk получил поддержку in-process NuGet клиента. Теперь через
#tool и InstallTool можно устанавливать любые NuGet пакеты прямо в инструменты.Добавлена поддержка
.slnx файлов. DotNetTest и парсинг решений теперь распознают XML-формат solution файлов, а автоопределение типа пути включает .slnx.Из других изменений: NuGetPack теперь поддерживает поле ReadMe для readme пакета, в AssemblyInfo creator можно добавлять несколько экземпляров одного атрибута, in-process NuGet restore заработал с аутентификацией через приватные фиды.
Есть breaking change для GitLab CI — поля CI_PIPELINE_ID и CI_BUILD_ID сменили тип с int на long, так как на gitlab.com идентификаторы давно вышли за пределы 32 бит.
📍 Навигация: Вакансии • Задачи • Собесы
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6❤🔥2❤2
💻 Утечки памяти в .NET
В .NET утечки памяти почти никогда не связаны с ручным управлением указателями. Они появляются из-за ошибок в управлении временем жизни объектов.
Главный инструмент .NET для освобождения ресурсов — интерфейс
Без using объект останется жить до следующего прохода GC, а ресурс под ним — ещё дольше:
Для асинхронных ресурсов — await using:
Что проверить в коде
Потоки. Каждый
Крупные аллокации. Массивы, буферы, большие строки — если они создаются в цикле, это быстро давит на GC. Здесь помогает
Object Pooling. Если объекты дорогие в создании и используются часто —
Простое правило: если тип реализует IDisposable, оборачиваем его в using. Остальное — профилировщик покажет.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#il_люминатор
В .NET утечки памяти почти никогда не связаны с ручным управлением указателями. Они появляются из-за ошибок в управлении временем жизни объектов.
Главный инструмент .NET для освобождения ресурсов — интерфейс
IDisposable и его асинхронный вариант IAsyncDisposable. Если объект реализует один из них, значит он держит что-то важное: файловый дескриптор, сетевое соединение, неуправляемую память. Без using объект останется жить до следующего прохода GC, а ресурс под ним — ещё дольше:
// Утечка: поток не закроется при исключении
var stream = new FileStream(path, FileMode.Open);
// Правильно: using гарантирует закрытие даже при исключении
using var stream = new FileStream(path, FileMode.Open);
Для асинхронных ресурсов — await using:
await using var resource = GetAsyncDisposable();
Что проверить в коде
Потоки. Каждый
Stream, StreamReader, StreamWriter должен быть обёрнут в using. Без этого файловые дескрипторы накапливаются.HttpClient. Один из самых частых источников проблем. Его нельзя создавать на каждый запрос, так как это исчерпывает пул сокетов.Крупные аллокации. Массивы, буферы, большие строки — если они создаются в цикле, это быстро давит на GC. Здесь помогает
ArrayPool<T> или System.Buffers.var pool = ArrayPool<byte>.Shared;
byte[] buffer = pool.Rent(4096);
try
{
// работа с буфером
}
finally
{
pool.Return(buffer);
}
Object Pooling. Если объекты дорогие в создании и используются часто —
ObjectPool<T> из Microsoft.Extensions.ObjectPool снижает нагрузку на аллокатор.Простое правило: если тип реализует IDisposable, оборачиваем его в using. Остальное — профилировщик покажет.
📍 Навигация: Вакансии • Задачи • Собесы
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍2
HttpClient.CurlDelegatingHandler — это обработчик в пайплайне HttpClient, который перехватывает запрос и возвращает готовую curl-команду в заголовке ответа. Никакой дополнительной логики писать не нужно.
Поддерживаются GET, POST, PUT и DELETE.
Как подключить:
dotnet add package HttpClient.CurlDelegatingHandler --version 1.0.0-alpha.1
Как использовать
Передаёте
CurlDelegatingHandler при создании клиента. Чтобы запрос не ушёл в сеть, добавляете заголовок CanSend: False. Curl-команда придёт обратно в заголовке outputCurl.using System.Text;
using CurlGenerator;
string url = "https://jsonplaceholder.typicode.com/posts";
string jsonPayload = @"{""title"": ""New Post"", ""body"": ""This is the body"", ""userId"": 1}";
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
var httpClient = new HttpClient(new CurlDelegatingHandler());
httpClient.DefaultRequestHeaders.Add(Settings.CanSend, "False");
var result = await httpClient.PostAsync(url, content);
string outputCurl = result.Headers.GetValues(Settings.OutputCurl).FirstOrDefault();
Console.WriteLine(outputCurl);
Вывод:
curl -X POST 'https://jsonplaceholder.typicode.com/posts' -H 'Content-Type: application/json' ...
DelegatingHandler встраивается в цепочку обработчиков HttpClient. Вместо того чтобы отправлять запрос по сети, он его перехватывает, строит из него curl-строку и возвращает её в заголовке ответа. Вся логика изолирована в одном месте.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤2🌚1
🤔 Спам тимлидам, фейковые офферы и приукрашенное резюме
IT-рынок 2026 года — это не конкурс честных и талантливых. Компании говорят об экологичной культуре, а сами гостят после четырёх этапов отбора.
На одну вакансию — тысяча откликов за сутки. В таких условиях выигрывают не самые опытные, а самые адаптивные.
➡️ Узнать приёмы тех, кто смог
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view
IT-рынок 2026 года — это не конкурс честных и талантливых. Компании говорят об экологичной культуре, а сами гостят после четырёх этапов отбора.
На одну вакансию — тысяча откликов за сутки. В таких условиях выигрывают не самые опытные, а самые адаптивные.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
😢4
Хороший код легко использовать правильно — и сложно использовать неправильно. Это важно при проектировании API: даже внутреннего.
На ревью кода интерфейсов проверяют логику, покрытие тестами, производительность. Но неудобный интерфейс это технический долг, который будет копиться каждый раз, когда кто-то будет использовать этот код.
Вот на что стоит обращать внимание:
• Понятен ли смысл метода. Название должно говорить само за себя без комментария над функцией и без погружения в реализацию.
Если приходится читать тело метода, чтобы понять что он делает, значит имя плохое.
• Предсказуемо ли поведение. Метод с одинаковыми параметрами должен возвращать одинаковый результат.
Скрытые побочные эффекты и неочевидные состояния — прямой путь к багам, которые воспроизводятся через раз.
• Булевые флаги это тревожный знак. Два булевых параметра подряд — почти всегда признак того, что функция делает слишком много или интерфейс не доработан:
ProcessData(bool flag1, bool flag2);
Явные типы — намерение читается сразу:
ProcessData(ProcessMode mode, ValidationOptions options);
• Исключения или типы результата. Исключения созданы для исключительных ситуаций, а типы результата для ожидаемых ошибок.
Смешивать их = заставлять пользователя угадывать, что пойдёт не так и в каком виде.
• Минимум обязательных параметров. Чем больше параметров нужно передать для базового вызова, тем выше порог входа.
Если для простого случая нужно заполнить пять аргументов, скорее всего интерфейс нуждается в умных значениях по умолчанию или отдельном упрощённом методе.
Удобный интерфейс это уважение к тем, кто будет с ним работать. И к себе через полгода, когда придётся вернуться к этому коду.
📍 Навигация: Вакансии • Задачи • Собесы
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14👍5🥱1🌚1
🗓 Merge conflict, но для календаря
В git есть автоматический резолв конфликтов. Теперь такое есть и для встреч — Microsoft Copilot в Outlook и Teams умеет сам переносить встречи, если в расписании возник конфликт.
Работает для личных встреч и 1:1. Групповые звонки, встречи длиннее 5 часов и повторяющиеся реже раза в месяц — пока не поддерживаются. Но для большинства рабочих 1:1 — уже удобно.
➡️ Источник
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
В git есть автоматический резолв конфликтов. Теперь такое есть и для встреч — Microsoft Copilot в Outlook и Teams умеет сам переносить встречи, если в расписании возник конфликт.
Работает для личных встреч и 1:1. Групповые звонки, встречи длиннее 5 часов и повторяющиеся реже раза в месяц — пока не поддерживаются. Но для большинства рабочих 1:1 — уже удобно.
📍 Навигация: Вакансии • Задачи • Собесы
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔6❤3😁2
🛠 Жизни зависимостей в ASP.NET
Одна из самых частых причин непредсказуемого поведения приложений на ASP.NET Core — неправильно выбранное время жизни сервиса. Проблема тихая: приложение запускается, тесты проходят, а в продакшне начинается что-то странное.
Три режима, которые нужно знать
В ASP.NET у каждого сервиса есть время жизни экземпляра.
Transient. Новый экземпляр создаётся каждый раз, когда сервис запрашивается из контейнера. Подходит для лёгких, stateless-сервисов без общего состояния.
Scoped. Один экземпляр на HTTP-запрос. Все зависимости внутри одного запроса получают один и тот же объект. Это стандартный выбор для большинства сервисов в веб-приложениях.
Singleton. Один экземпляр на всё время жизни приложения. Создаётся один раз и переиспользуется во всех запросах.
Где ломается
Самая распространённая ошибка — инжектировать Scoped-сервис в синглтон. Выглядит невинно, но это называется captive dependency, и ASP.NET даже выбросит исключение при старте, если включена валидация.
Синглтон захватывает Scoped-сервис при первом создании и держит его до конца жизни приложения. Scoped-сервис при этом теряет свой контекст, например DbContext, и начинает работать непредсказуемо.
Если нужен Scoped-сервис внутри синглтона, используйте
И включите валидацию зависимостей в dev-окружении — ASP.NET поймает большинство проблем ещё при запуске.
Если не уверены в выборе, начинайте со Scoped. Для веб-приложений это безопасный дефолт. Singleton используйте только тогда, когда явно нужно общее состояние, и убедитесь, что сервис действительно thread-safe. Transient подходит для простых утилит без состояния.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view
Одна из самых частых причин непредсказуемого поведения приложений на ASP.NET Core — неправильно выбранное время жизни сервиса. Проблема тихая: приложение запускается, тесты проходят, а в продакшне начинается что-то странное.
Три режима, которые нужно знать
В ASP.NET у каждого сервиса есть время жизни экземпляра.
Transient. Новый экземпляр создаётся каждый раз, когда сервис запрашивается из контейнера. Подходит для лёгких, stateless-сервисов без общего состояния.
Scoped. Один экземпляр на HTTP-запрос. Все зависимости внутри одного запроса получают один и тот же объект. Это стандартный выбор для большинства сервисов в веб-приложениях.
Singleton. Один экземпляр на всё время жизни приложения. Создаётся один раз и переиспользуется во всех запросах.
Где ломается
Самая распространённая ошибка — инжектировать Scoped-сервис в синглтон. Выглядит невинно, но это называется captive dependency, и ASP.NET даже выбросит исключение при старте, если включена валидация.
// Так делать не надо
public class MySingleton
{
private readonly IScopedService _scoped;
public MySingleton(IScopedService scoped) // Проблема здесь
{
_scoped = scoped;
}
}
Синглтон захватывает Scoped-сервис при первом создании и держит его до конца жизни приложения. Scoped-сервис при этом теряет свой контекст, например DbContext, и начинает работать непредсказуемо.
Если нужен Scoped-сервис внутри синглтона, используйте
IServiceScopeFactory:public class MySingleton
{
private readonly IServiceScopeFactory _scopeFactory;
public MySingleton(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
public void DoWork()
{
using var scope = _scopeFactory.CreateScope();
var scoped = scope.ServiceProvider.GetRequiredService<IScopedService>();
scoped.Execute();
}
}
И включите валидацию зависимостей в dev-окружении — ASP.NET поймает большинство проблем ещё при запуске.
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = true;
options.ValidateOnBuild = true;
});
Если не уверены в выборе, начинайте со Scoped. Для веб-приложений это безопасный дефолт. Singleton используйте только тогда, когда явно нужно общее состояние, и убедитесь, что сервис действительно thread-safe. Transient подходит для простых утилит без состояния.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍1👾1
C# backend-разработчик в команду Security — Офис/гибрид в Москве
Backend Engineer — от 175 000 до 325 000 ₽, удалёнка
C#/.NET разработчик — от 350 000 ₽, удалёнка
Бустер — Удалённо в любом городе мира.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
Всё начинается разумно. В нескольких сервисах повторяется одна логика — контекст БД, аудит, логирование. Выносишь в базовый класс. Код короче, дублирования нет.
Проходит полгода. Простой юнит-тест поднимает глобальную шину событий и запускает рефлексию. Никто не просил. Просто создали экземпляр репозитория.
Что происходит в конструкторе:
public abstract class RepositoryBase<TEntity>
{
protected RepositoryBase(DbContext context)
{
Context = context;
ApplyEntityConfiguration(); // рефлексия
SubscribeToAuditEvents(); // подписка на события
}
}
Производный класс просто вызвал
base() — и получил в нагрузку поведение, которое не запрашивал. Это невидимая связанность: поведение определяется не сигнатурой, а тем, что спрятано в цепочке наследования.Особый случай — глобальное состояние
Статический инициализатор внутри базового класса меняет сериализацию для всего приложения при первой загрузке любого наследника. Не локально — глобально. Молча.
Наследование vs композиция
Глубокое дерево:
RepositoryBase → MongoBase → AuditableBase → ProductRepository. Четыре уровня. Поведение на каждом. Ничего не видно на месте вызова.Декоратор решает то же самое явно:
public class AuditingRepository : IProductRepository
{
private readonly IProductRepository _inner;
private readonly IAuditService _audit;
public async Task<Product> GetByIdAsync(Guid id)
{
await _audit.LogAccessAsync(id);
return await _inner.GetByIdAsync(id);
}
}
Каждый слой делает одну вещь. Зависимости видны. Убрать логирование можно не трогая репозиторий.
Глобальная конфигурация — в точку запуска
Если что-то влияет на всё приложение, оно должно жить в
Program.cs, а не в базовом классе, который загружается неизвестно когда.Базовый класс становится проблемой, когда берёт на себя слишком много. В этот момент он перестаёт быть абстракцией и превращается в объект бога.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5💯3🔥2
Разработчик запустил nugetz.dev — альтернативный интерфейс для поиска по реестру NuGet.
Стандартный поиск на nuget.org работает, но медленно и неудобно. Когда выбираешь библиотеку, хочешь быстро понять: пакет живой, как давно обновлялся, насколько популярен. Сейчас это занимает лишнее время.
Nugetz решает именно это. Тот же реестр, но с чистым интерфейсом, быстрой выдачей и без лишнего шума на экране. Без регистрации и рекламы.
Идея пришла от npmx — похожего инструмента для npm-экосистемы. Rodrigo просто спросил себя, почему у .NET такого нет, и сделал.
📍 Навигация: Вакансии • Задачи • Собесы
#пульс_индустрии
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13👍1
This media is not supported in your browser
VIEW IN TELEGRAM
😢2❤1