ch
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