uk
Feedback
C# (C Sharp) programming

C# (C Sharp) programming

Відкрити в Telegram

По всем вопросам- @notxxx1 Реестр РКН: https://clck.ru/3Fk3kb #VRHSZ

Показати більше

📈 Аналітичний огляд Telegram-каналу C# (C Sharp) programming

Канал C# (C Sharp) programming (@csharp_ci) у мовному сегменті Російська є активним учасником. На даний момент спільнота об'єднує 18 319 підписників, посідаючи 7 281 місце в категорії Технології та додатки та 36 747 місце у регіоні Росія.

📊 Показники аудиторії та динаміка

З моменту свого створення невідомо, проект продемонстрував стрімке зростання, зібравши аудиторію у 18 319 підписників.

За останніми даними від 23 червня, 2026, канал демонструє стабільну активність. Хоча за останні 30 днів спостерігається зміна кількості учасників на 27, а за останні 24 години на 4, загальне охоплення залишається високим.

  • Статус верифікації: Не верифікований
  • Рівень залученості (ER): Середній показник залученості аудиторії становить 17.49%. Протягом перших 24 годин після публікації контент зазвичай збирає 7.97% реакцій від загальної кількості підписників.
  • Охоплення публікацій: В середньому кожен допис отримує 3 204 переглядів. Протягом першої доби публікація в середньому набирає 1 460 переглядів.
  • Реакції та взаємодія: Аудиторія активно підтримує контент: середня кількість реакцій на один пост – 0.
  • Тематичні інтереси: Контент зосереджений навколо ключових тем, таких як .net, api, логика, архитектура, string.

📝 Опис та контентна політика

Автор описує ресурс як майданчик для висловлення суб'єктивної думки:
По всем вопросам- @notxxx1 Реестр РКН: https://clck.ru/3Fk3kb #VRHSZ

Завдяки високій частоті оновлень (останні дані отримано 24 червня, 2026), канал підтримує актуальність та високий рівень охоплення публікацій. Аналітика показує, що аудиторія активно взаємодіє з контентом, що робить його важливою точкою впливу в категорії Технології та додатки.

18 319
Підписники
+424 години
+177 днів
+2730 день

Триває завантаження даних...

Залучення підписників
червень '26
червень '26
+136
в 3 каналах
травень '26
+131
в 2 каналах
Get PRO
квітень '26
+76
в 1 каналах
Get PRO
березень '26
+96
в 23 каналах
Get PRO
лютий '26
+112
в 45 каналах
Get PRO
січень '26
+398
в 132 каналах
Get PRO
грудень '25
+117
в 5 каналах
Get PRO
листопад '25
+318
в 44 каналах
Get PRO
жовтень '25
+84
в 14 каналах
Get PRO
вересень '25
+162
в 37 каналах
Get PRO
серпень '25
+223
в 9 каналах
Get PRO
липень '25
+418
в 77 каналах
Get PRO
червень '25
+293
в 71 каналах
Get PRO
травень '25
+238
в 1 каналах
Get PRO
квітень '25
+191
в 28 каналах
Get PRO
березень '25
+185
в 2 каналах
Get PRO
лютий '25
+210
в 42 каналах
Get PRO
січень '25
+200
в 46 каналах
Get PRO
грудень '24
+416
в 56 каналах
Get PRO
листопад '24
+1 096
в 187 каналах
Get PRO
жовтень '24
+1 800
в 73 каналах
Get PRO
вересень '24
+635
в 199 каналах
Get PRO
серпень '24
+662
в 44 каналах
Get PRO
липень '24
+665
в 53 каналах
Get PRO
червень '24
+1 168
в 60 каналах
Get PRO
травень '24
+565
в 39 каналах
Get PRO
квітень '24
+569
в 45 каналах
Get PRO
березень '24
+709
в 21 каналах
Get PRO
лютий '24
+1 055
в 2 каналах
Get PRO
січень '24
+1 376
в 46 каналах
Get PRO
грудень '23
+608
в 44 каналах
Get PRO
листопад '23
+1 091
в 30 каналах
Get PRO
жовтень '23
+332
в 19 каналах
Get PRO
вересень '23
+352
в 0 каналах
Get PRO
серпень '23
+1 949
в 0 каналах
Get PRO
липень '23
+507
в 0 каналах
Get PRO
червень '23
+1 238
в 0 каналах
Get PRO
травень '23
+1 891
в 0 каналах
Get PRO
квітень '23
+3 025
в 0 каналах
Get PRO
березень '23
+1 858
в 0 каналах
Get PRO
лютий '23
+85
в 0 каналах
Get PRO
січень '23
+113
в 0 каналах
Get PRO
грудень '22
+171
в 0 каналах
Get PRO
листопад '22
+226
в 0 каналах
Get PRO
жовтень '22
+197
в 0 каналах
Get PRO
вересень '22
+170
в 0 каналах
Get PRO
серпень '22
+756
в 0 каналах
Get PRO
липень '22
+82
в 0 каналах
Get PRO
червень '22
+152
в 0 каналах
Get PRO
травень '22
+344
в 0 каналах
Get PRO
квітень '22
+256
в 0 каналах
Get PRO
березень '22
+53
в 0 каналах
Get PRO
лютий '22
+45
в 0 каналах
Get PRO
січень '22
+97
в 0 каналах
Get PRO
грудень '21
+45
в 0 каналах
Get PRO
листопад '21
+1 110
в 0 каналах
Дата
Залучення підписників
Згадування
Канали
24 червня+1
23 червня+11
22 червня+10
21 червня+4
20 червня+3
19 червня+4
18 червня+11
17 червня+5
16 червня+3
15 червня+5
14 червня+4
13 червня+8
12 червня+6
11 червня+8
10 червня+10
09 червня0
08 червня0
07 червня+2
06 червня+3
05 червня+12
04 червня+10
03 червня+8
02 червня+4
01 червня+4
Дописи каналу
Совет по .NET Aspire: не воспринимайте его только как удобную локальную панель. Самая полезная часть начинается, когда у прил
Совет по .NET Aspire: не воспринимайте его только как удобную локальную панель. Самая полезная часть начинается, когда у приложения появляется инфраструктура: API, Postgres, Redis, фоновые сервисы, переменные окружения и connection strings. Вместо того чтобы вручную собирать docker-compose.yml, опишите сервисы в AppHost. Aspire Docker publisher сможет сгенерировать Compose-артефакты из этой модели. Но важно понимать границу: Aspire не деплоит приложение за вас. Он не заменяет CI/CD, не управляет секретами и не переносит контейнеры на сервер. Вам всё равно нужно собрать image, задать реальные env-переменные, скопировать файлы и запустить Docker Compose. Зато это хороший баланс: меньше ручной YAML-рутины, но без магии, которая скрывает реальную схему деплоя.

2
🖥 На Stepik обновили курс «C# с нуля до профи» Представьте: через четыре месяца вы открываете чужой .NET-проект и читаете ег
🖥 На Stepik обновили курс «C# с нуля до профи» Представьте: через четыре месяца вы открываете чужой .NET-проект и читаете его как книгу. IServiceCollection не вызывает ступора. async Task<IActionResult> пишется на автомате. Вы точно знаете, почему EF Core сгенерировал именно такой SQL - и как переписать запрос, чтобы он летал. Это не фантазия. Это результат после 16 модулей, в которых каждая концепция объясняется через код и закрепляется практикой. ООП, SOLID, LINQ, async/await, DI, EF Core, ASP.NET Core, Docker, Kubernetes - всё, что казалось магией, станет рабочим инструментом. А бонусом - портфолио проектов: от CLI-утилит и REST API до собственного SaaS с multi-tenancy, JWT и деплоем в Kubernetes под TLS. Скидка - 58% доступна 48 часов: https://stepik.org/a/282984/
1 466
3
Тест прошёл. А PostgreSQL вообще в курсе? Интеграционные тесты часто выглядят надёжно ровно до того момента, пока приложение
Тест прошёл. А PostgreSQL вообще в курсе? Интеграционные тесты часто выглядят надёжно ровно до того момента, пока приложение не встречается с настоящей базой. На локалке всё зелёное. В CI всё зелёное. Моки довольны. In-memory база тоже не против. А потом в проде внезапно выясняется, что реальный PostgreSQL иначе обрабатывает запрос, constraint не даёт сохранить данные, транзакция ведёт себя не так, как ожидалось, а Redis показывает проблему, которую тесты вообще не могли поймать. Именно поэтому Testcontainers в .NET так хорошо заходят для интеграционных тестов. Вместо имитации базы вы поднимаете настоящий PostgreSQL, Redis или другой сервис в Docker-контейнере, прогоняете приложение против реальной зависимости и удаляете контейнер после тестов. Это даёт намного больше уверенности, чем тесты против подмены. При этом не нужен общий тестовый сервер, который кто-то сломал, не почистил или настроил иначе. В хорошей схеме контейнеры запускаются через fixture, приложение получает connection string динамически, версии образов фиксируются, а настройка прячется за небольшими helper-классами. Сам тест при этом остаётся читаемым: он проверяет бизнес-сценарий, а не превращается в простыню из настройки базы и очистки состояния. Есть важный нюанс. Общие fixtures ускоряют тесты, но требуют дисциплины со shared state. Когда изоляция важнее скорости, лучше использовать отдельные fixtures и не ловить фантомные падения из-за данных, оставшихся от соседнего теста. Мне нравится этот подход именно за баланс. Вы тестируете не идеальную игрушечную модель приложения, а поведение, максимально близкое к реальному окружению. Но без боли ручной инфраструктуры. Поэтому в следующий раз, когда интеграционный тест прошёл против in-memory базы, стоит задать неприятный вопрос: а настоящая база с ним согласится?
1 774
4
Кеширование в ASP.NET Core: от IMemoryCache до Redis Приложение работает быстро — пока растёт нагрузка на базу, увеличивается
Кеширование в ASP.NET Core: от IMemoryCache до Redis Приложение работает быстро — пока растёт нагрузка на базу, увеличивается время ответа API, а масштабирование инфраструктуры не начинает обходиться слишком дорого. Кеширование помогает снизить количество запросов к хранилищам, ускорить работу сервисов и эффективнее использовать ресурсы. Но результат зависит от того, какие данные попадают в кеш, где он хранится и как устроена инвалидация. На открытом вебинаре разберём: — какие данные стоит кешировать, а какие — нет; — как выбрать стратегию инвалидации; — как работают HTTP Cache, UseResponseCaching и IMemoryCache в ASP.NET Core; — когда нужен распределённый кеш через IDistributedCache; — как использовать Redis и чем он отличается от Memcached. Открытый урок пройдёт 25 июня в 20:00 МСК в преддверии старта курса «C# ASP.NET Core разработчик». Подробности и регистрация: https://otus.pw/wSp1/?erid=2W5zFJU5ydB Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
1 429
5
Аллокации, которых нет в коде: охота на скрытый боксинг в .NET 10 Самая дорогая аллокация в вашем сервисе та, которой нет в и
Аллокации, которых нет в коде: охота на скрытый боксинг в .NET 10 Самая дорогая аллокация в вашем сервисе та, которой нет в исходниках. Вы написали struct ради zero-allocation, прошли code review, а в проде Gen0-коллекции все равно идут косяком. Потому что между вашим кодом и машинным кодом стоит компилятор, и он молча упаковывает ваш value-тип в кучу там, где вы этого не просили — а на код-ревью этого не видно. TL;DR. Боксинг (boxing) в .NET - это не только object o = 42. Он прячется в вызовах интерфейсных методов на struct, в дефолтном ValueType.Equals, в params object[]-аргументах, в foreach по интерфейсу и в замыканиях. При этом часть “классических” примеров боксинга из старых гайдов на современном рантайме уже не аллоцирует — JIT научился их вырезать, и слепо копировать советы десятилетней давности вредно. Ниже — карта мест, где боксинг живёт и сейчас, отдельный разбор того, что рантайм уже оптимизировал, реальный мини-кейс, воспроизводимый бенчмарк на BenchmarkDotNet с MemoryDiagnoser, способ ловить упаковку через DOTNET_JitDisasm и dotnet-gcdump, и паттерны лечения без потери читаемости. О версиях и числах. Всё прверялось на .NET 10 (текущий LTS) и C# 13/14-уровне компилятора, Release, без отладчика, BenchmarkDotNet с MemoryDiagnoser. На .NET 8/9 поведение в основном такое же, но отдельные оптимизации JIT отличаются между мажорными версиями — поэтому главный принцип статьи: не верьте на слово (в том числе мне), гоняйте MemoryDiagnoser на своей версии рантайма. Числа в таблицах ниже - иллюстративные, порядок величины, а не точные замеры с вашего железа. Пролог: “у нас же всё на struct, откуда Gen0?” Сервис на горячем пути считает метрики: миллионы маленьких readonly struct-значений в секунду, никакого new, никаких классов в hot path. По задумке — ноль аллокаций. На дашборде — стабильный поток Gen0-коллекций раз в несколько секунд под нагрузкой. Профайлер показывает аллокации, но стек ведёт в метод, где в коде нет ни одного new. Там цикл по интерфейсу, пара вызовов .Equals(), передача значения в params-метод лога. Глазами — чисто. В машинном коде — box-инструкции на каждой итерации. Это и есть скрытый боксинг: компилятор C# и JIT упаковывают ваш struct в объект на куче, потому что в конкретной точке кода value-тип нужно представить как ссылочный. Симптом — Gen0-коллекции “из ниоткуда”, и его не видно ни в code review, ни в дампе, пока не посмотришь на IL или дизасм. Если тема близка - я регулярно разбираю такие штуки по C# и .NET (внутренности рантайма, перформанс, неочевидные грабли с замерами и дизасмом) в своём Telegram-канале: t.me/csharp_ci. Заходите, если интересно копаться глубже. Что такое боксинг и почему он стоит дорого Боксинг — это упаковка value-типа (struct, enum, примитив) в объект на управляемой куче. Рантайму нужно выделить заголовок объекта, скопировать туда значение и вернуть ссылку. Анбоксинг - обратная операция с проверкой типа. Цена не в самой инструкции, а в последствиях: каждая упаковка - это аллокация в Gen0. Много мелких аллокаций на горячем пути означают частые Gen0-коллекции, паузы (пусть и короткие), вытеснение полезных данных из кэша и общий рост CPU на ровном месте. На сервисе с SLA по p99 это бьёт по хвосту латентности так же, как и любая другая лишняя аллокация. В IL боксинг виден явно - инструкция box. Именно её мы и будем искать. Читать дальше: https://habr.com/ru/articles/1049236/
1 765
6
⚡️ Геймдеверы, обновляемся: Unreal Engine 5.8 уже вышел Epic Games выпустила Unreal Engine 5.8. Ссылка: https://www.unrealeng
⚡️ Геймдеверы, обновляемся: Unreal Engine 5.8 уже вышел Epic Games выпустила Unreal Engine 5.8. Ссылка: https://www.unrealengine.com/news/unreal-engine-5-8-is-now-available Главное обновление для всех, кто следит за AI в геймдеве: в движок добавили поддержку MCP. Теперь Claude, Gemini и другие AI-агенты могут напрямую подключаться к Unreal Engine, видеть структуру проекта и выполнять задачи внутри редактора. Не просто советовать в чате, а реально работать с сценой. На демо агент создаёт целый городской квартал прямо в Unreal Editor. Это уже не «ИИ поможет написать промпт», а шаг к агентам, которые собирают уровни, прототипируют локации, правят ассеты и ускоряют production pipeline. Похоже, поток AI-контента в играх только начинается. Скачать: https://www.unrealengine.com/download
3 334
7
Fenwick Tree на C#: всё держится на одном битовом трюке Fenwick Tree, или Binary Indexed Tree, считает prefix sums за O(log n
Fenwick Tree на C#: всё держится на одном битовом трюке Fenwick Tree, или Binary Indexed Tree, считает prefix sums за O(log n). Главная операция: i & -i Она находит младший установленный бит числа. Именно это значение говорит структуре, на сколько нужно прыгнуть по индексам. Пример: i = 12 // 1100 i & -i = 4 // 0100 Реализация на C#: public sealed class FenwickTree { private readonly int[] _tree; public FenwickTree(int size) { _tree = new int[size + 1]; } public void Update(int index, int delta) { while (index < _tree.Length) { _tree[index] += delta; index += index & -index; } } public int Query(int index) { var sum = 0; while (index > 0) { sum += _tree[index]; index -= index & -index; } return sum; } } Как это работает: * Update идёт вверх по структуре и обновляет все узлы, которые отвечают за индекс * Query идёт вниз и собирает блоки, из которых состоит prefix sum * index & -index каждый раз выбирает размер текущего блока Главный нюанс: Fenwick Tree обычно использует 1-based indexing. То есть первый элемент имеет индекс 1, а не 0. Пример использования: var tree = new FenwickTree(5); tree.Update(1, 10); tree.Update(2, 20); tree.Update(3, 30); Console.WriteLine(tree.Query(3)); // 60 Красота Fenwick Tree в том, что дерево не хранится явно. Нет узлов. Нет ссылок. Нет рекурсии. Только массив и один битовый трюк. Дерево спрятано прямо внутри двоичного представления индексов.
2 409
8
C# вопрос с собеседований: скомпилируется ли этот код? На первый взгляд строка выглядит криво: order._items.AddRange(items); Поле _items объявлено как private. Значит ли это, что доступ к нему разрешён только через this._items? Нет. Код скомпилируется. В C# модификатор private ограничивает доступ типом, а не конкретным экземпляром объекта. То есть любой код внутри класса Order может обращаться к private-полям любого другого экземпляра Order. Пример: public class Order { private readonly List<OrderItem> _items = new(); public void CopyItemsFrom(Order other) { _items.AddRange(other._items); } } Здесь other._items тоже валиден, потому что мы всё ещё находимся внутри типа Order. Это часто путают на собеседованиях: private означает не «доступно только этому объекту», а «доступно только коду внутри этого класса». В примере это используется в static factory method: var order = new Order { ... }; order._items.AddRange(items); return order; Метод Create находится внутри Order, поэтому он имеет полный доступ к private-состоянию создаваемого экземпляра. Более интересный вопрос тут даже не в компиляции, а в дизайне. Такой подход часто встречается в DDD: * private constructor * static factory method * закрытая коллекция _items * наружу отдаётся IReadOnlyList<OrderItem> * изменение состояния контролируется внутри агрегата Но есть нюанс. _items.AsReadOnly() каждый раз создаёт новый wrapper. Обычно лучше кэшировать read-only view или возвращать IReadOnlyCollection<T>, если индексатор не нужен. Ещё важнее: фабрика должна не просто копировать items, а проверять инварианты: if (items is null || items.Count == 0) throw new DomainException("Order must contain at least one item."); Иначе получается не DDD entity, а просто объект с красивой фабрикой. Да, код компилируется. Потому что private в C# работает на уровне типа, а не экземпляра. А хороший senior-вопрос здесь такой: скомпилируется ли код - это база. А вот защищает ли этот Order свои инварианты - уже архитектура.
2 379
9
Приходи на C# Speed Dating — 2 часа на полезные знакомства 23 июня пройдет вечер коротких онлайн-знакомств для C#-разработчик
Приходи на C# Speed Dating — 2 часа на полезные знакомства 23 июня пройдет вечер коротких онлайн-знакомств для C#-разработчиков. Как все пройдет Участники будут рандомно делиться по парам и общаться в Zoom. Будет 6 раундов по 10 минут. Зачем приходить — обсудишь темы, которые вызывают споры: AI, карьера, архитектурные паттерны и метрики. — заберешь идеи и практики, которые работают у других, и поделишься своим опытом. — найдешь полезные контакты и познакомишься с C#-коммьюнити. Вечер организуют ребята из Mindbox, но они будут «без оружия»: никакого хантинга и рассказов про вакансии, пока ты сам не спросишь. 📅 23 июня ⏰ 19:00–21:00 по мск 📍 Zoom (пришлем ссылку после регистрации) 👉 Зарегистрироваться
2 179
10
OptimizerDuck - open-source утилита, после которой CCleaner уже не нужен OptimizerDuck собирает в одном приложении 30+ твиков
OptimizerDuck - open-source утилита, после которой CCleaner уже не нужен OptimizerDuck собирает в одном приложении 30+ твиков системы: от отключения телеметрии, Copilot, Cortana и рекламного ID до тонкой настройки автозагрузки, служб, питания и задержек ввода. Укаждой настройки есть рейтинг риска. То есть вы заранее видите, что безопасно применить, а где лучше подумать, вместо классического сценария «нажал всё подряд и потом откатываешь систему». Что умеет: * отключать телеметрию Windows, Cortana, Copilot и рекламный ID * управлять автозагрузкой приложений * настраивать службы хоста под объём RAM * включать кастомный план питания для высокой производительности * снижать задержку клавиатуры для игр * применять GPU-твики, которые обычно правят вручную через реестр Все изменения обратимы. Не понравилось, можно откатить назад. можно откатить назад. https://github.com/itsfatduck/optimizerDuck
2 272
11
Program.cs — это не просто точка входа. За несколькими строками кода в ASP.NET Core скрывается полноценная инфраструктура зап
Program.cs — это не просто точка входа. За несколькими строками кода в ASP.NET Core скрывается полноценная инфраструктура запуска приложений, управления жизненным циклом и фоновых процессов. На открытом уроке разберём, как на самом деле устроен ASP.NET Core и почему понимание Generic Host меняет подход к разработке .NET-приложений. Поговорим о жизненном цикле приложения, фоновых задачах через IHostedService и различиях между веб-приложениями и консольными сервисами. Это особенно полезно разработчикам, которые уже работают с ASP.NET Core, но хотят глубже понимать архитектуру платформы, увереннее проектировать сервисы и принимать технические решения осознанно, а не по шаблону. Открытый урок пройдёт 18 июня в 20:00 МСК в преддверии старта курса «C# ASP.NET Core разработчик». Подробности и регистрация: https://otus.pw/SMEy/ Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
1 616
12
⚡️ Переписывать legacy-систему редко значит «переписать код». Обычно самая дорогая часть начинается там, где старый и новый м
⚡️ Переписывать legacy-систему редко значит «переписать код». Обычно самая дорогая часть начинается там, где старый и новый мир должны какое-то время жить одновременно. Типичная проблема - синхронизация данных между старой БД и новой моделью. На бумаге кажется, что можно взять CDC, подключить Debezium, прокинуть события и жить спокойно. На практике это работает только пока у вас почти прямое соответствие: таблица → событие → таблица. В реальном legacy всё еще хуже. Одна запись в старой системе может собираться из нескольких агрегатов в новой. Поля могут иметь другой смысл. Часть данных нормализована, часть размазана по справочникам, часть хранится как «магические» статусы. А ещё при переносе нужно не просто скопировать байты, а применить бизнес-правила: пересчитать состояние, отфильтровать мусор, восстановить инварианты, иногда даже специально повторить старый баг, потому что на нём завязан внешний процесс. Нормальное решение может выглядеть так: * события из новой системы публикуются через outbox, а не напрямую из хендлера * синхронизатор читает сообщения из RabbitMQ или другого брокера * трансформации делаются явно, через application service или отдельный mapping layer * операции проектируются идемпотентными, потому что повторная доставка будет всегда * для каждой внешней записи хранится mapping старого и нового идентификатора * ошибки не теряются, а уходят в retry/DLQ с понятной диагностикой * консистентность проверяется отдельными reconciliation jobs, а не верой в «оно доедет» Такой синхронизатор выглядит как временный костыль, но по сложности быстро становится полноценной подсистемой. У него появляются свои контракты, версии сообщений, миграции, алерты, метрики, ручные repair-команды и отдельные сценарии восстановления после падений. Если всё сделано хорошо, этот компонент потом удалят. Он нужен только на период миграции. Но если сделать его плохо, миграция не закончится никогда.
2 733
13
🖥 Задача using System; using System.Collections.Generic; using System.Threading.Tasks; var actions = new List&gt;();
🖥 Задача using System; using System.Collections.Generic; using System.Threading.Tasks; var actions = new List<Func<Task>>(); for (int i = 0; i < 3; i++) { actions.Add(async () => { await Task.Yield(); Console.Write(i + " "); }); } foreach (var action in actions) { await action(); } Что выведет код? Варианты: 0 1 2 3 3 3 0 0 0 1 2 3 Правильный ответ: 3 3 3 Разбор коротко: i в for не копируется в каждую лямбду. Все три лямбды захватывают одну и ту же переменную i. Когда цикл закончился, i == 3. Поэтому каждая отложенная async-функция печатает уже финальное значение. Чтобы получить 0 1 2, нужно создать локальную копию внутри цикла: ``` for (int i = 0; i < 3; i++) { int copy = i; actions.Add(async () => { await Task.Yield(); Console.Write(copy + " "); }); } ```
2 742
14
🐳 «Используй Testcontainers вместо in-memory» - это только половина правды Все уже выучили: EF Core InMemory provider - не интеграционный тест. Он не ловит: - баги в LINQ-трансляции - ограничения БД - коллации - реальные типы колонок - поведение конкретного SQL-провайдера Окей, заменили на реальный PostgreSQL через Testcontainers. Победа? Не совсем. Вот что начинается дальше. 1. Вы получили «медленное враньё» вместо «быстрого» Поднимать контейнер на каждый тест-класс - быстрый способ превратить CI из 30 секунд в 8 минут. Нормальный вариант: - один контейнер на всю тестовую сессию - изоляция данных между тестами через Respawn - без пересоздания базы и контейнера каждый раз Respawn чистит таблицы с учётом графа foreign keys за миллисекунды. 2. Транзакционный откат ≠ реальный сценарий Трюк «обернули тест в транзакцию и откатили» красиво выглядит, но ломается, когда в коде есть: - свои транзакции - несколько SaveChanges - фоновые операции - поведение, завязанное на commit В итоге тестируется сценарий, которого в проде нет. 3. Самая коварная ловушка - общий DbContext Если тест и код используют один экземпляр DbContext, EF может вернуть данные из change tracker, а не из базы. Тест зелёный, но он врёт: реальный SQL-запрос мог вообще не выполниться. Между Act и Assert стоит чистить трекер: Db.ChangeTracker.Clear(); 4. Бонус, который теряют 90% команд - тест миграций Реальная БД позволяет прогнать EF-миграции на чистой схеме. Если миграция падает или схема разъехалась с моделью, вы узнаёте об этом в CI, а не в проде в пятницу вечером. Пример базового подхода: public class IntegrationTestBase : IAsyncLifetime { private static readonly PostgreSqlContainer _db = new PostgreSqlBuilder() .WithImage("postgres:16-alpine") .Build(); private Respawner _respawner = null!; protected AppDbContext Db = null!; public async Task InitializeAsync() { await _db.StartAsync(); var options = new DbContextOptionsBuilder<AppDbContext>() .UseNpgsql(_db.GetConnectionString()) .Options; Db = new AppDbContext(options); // Реальные миграции - заодно проверяем, что они накатываются await Db.Database.MigrateAsync(); await using var conn = new NpgsqlConnection(_db.GetConnectionString()); await conn.OpenAsync(); _respawner = await Respawner.CreateAsync(conn, new RespawnerOptions { DbAdapter = DbAdapter.Postgres, SchemasToInclude = ["public"] }); } // Сброс данных перед каждым тестом - без пересоздания контейнера protected async Task ResetAsync() { await using var conn = new NpgsqlConnection(_db.GetConnectionString()); await conn.OpenAsync(); await _respawner.ResetAsync(conn); // Иначе тест может читать из кеша, а не из БД Db.ChangeTracker.Clear(); } public Task DisposeAsync() => Task.CompletedTask; } Testcontainers - это не галочка «best practice», а смена философии. Без нормальной изоляции данных вы просто пересели с быстрого вранья на медленное. А как вы изолируете состояние БД между интеграционными тестами - Respawn, транзакции или пересоздание контейнера? #dotnet #csharp #testing #efcore
2 746
15
Алгоритму почти 70 лет, а он до сих пор живёт в ядре Linux. В 1957 году Wilkes, Wheeler и Gill описали быстрый способ считать
Алгоритму почти 70 лет, а он до сих пор живёт в ядре Linux. В 1957 году Wilkes, Wheeler и Gill описали быстрый способ считать количество установленных битов в числе. Не циклом по одному биту, а через маски и арифметику сразу над группами битов. Идея простая: - сначала считаем биты парами - потом группами по 4 - потом по байтам - в конце умножение собирает сумму в старший байт Если в процессоре нет инструкции POPCNT, Linux использует похожий подход в __sw_hweight64. Красивый пример того, как старый битовый трюк пережил десятилетия и всё ещё работает в современном системном коде.
3 642
16
#ПятничныйКвиз #ДляСамыхМаленьких
4 428
17
✔️ Одна строчка .Result роняет ваш ASP.NET Core при CPU 8 %: разбор hill-climbing в .NET 9 TL;DR. Один foo.GetAsync().Result
✔️ Одна строчка .Result роняет ваш ASP.NET Core при CPU 8 %: разбор hill-climbing в .NET 9 TL;DR. Один foo.GetAsync().Result внутри middleware превращает ASP.NET Core, державший 50k RPS на p99 = 40 мс, в сервис на 12k RPS с p99 = 4 с при CPU 8 %. Виноват не блокирующий вызов сам по себе. Виноват hill-climbing: фидбэк-луп в ThreadPool, внутри которого живёт дискретное преобразование Фурье. Разбираемся по исходникам CoreCLR, как это работает, воспроизводим эффект на ~80 строках кода и показываем, почему SetMinThreads это не лечение, а анестезия. https://habr.com/ru/articles/1040804/
4 593
18
🖥 C# задачка с подвохом Что выведет код? using System; using System.Collections.Generic; var list = new List&gt;(); f
🖥 C# задачка с подвохом Что выведет код? using System; using System.Collections.Generic; var list = new List<Func<int>>(); for (int i = 0; i < 3; i++) { int x = i; list.Add(() => x); x = 100; } foreach (var f in list) { Console.Write(f() + " "); } A) 0 1 2 😎 100 100 100 C) 3 3 3 D) 0 100 100 Правильный ответ: 😎 100 100 100 Почему так: Внутри каждой итерации создаётся новая локальная переменная x, и именно её захватывает лямбда. Кажется, что ответы должны быть 0 1 2, потому что x получает значение i. Но после добавления лямбды переменная x всё ещё та же самая захваченная переменная. Потом мы меняем её на 100. В итоге каждая лямбда хранит свою отдельную x, но каждая из этих x была изменена на 100.
4 354
19
🖥 Сервисы крутятся. Прод вроде живой. Но когда тимлид спрашивает: «почему здесь лучше ValueTask, а не Task?» или «как GC пов
🖥 Сервисы крутятся. Прод вроде живой. Но когда тимлид спрашивает: «почему здесь лучше ValueTask, а не Task?» или «как GC поведёт себя под нагрузкой?» - ты начинаешь плыть. И дело не в том, что ты плохо пишешь код. Просто большинство курсов заканчиваются ровно там, где начинается настоящий .NET. Этот курс про то, что обычно остаётся под капотом: - CLR - JIT - GC - Span - async state machine - Source Generators - lock-free подходы - OpenTelemetry - дампы в проде На практике разбираем, как .NET реально работает внутри: что происходит с кодом после компиляции, как память живёт под нагрузкой, почему async иногда помогает, а иногда ломает производительность, как читать проблемы по дампам и метрикам, а не гадать по логам. Если хочешь дойти до уровня, где система для тебя не чёрный ящик, а инструмент, который ты понимаешь до IL, - велкам. Сейчас на stepik доступна скидка 55%: https://stepik.org/a/288694
3 410
20
Немає тексту...
3 892