es
Feedback
C# (C Sharp) programming

C# (C Sharp) programming

Ir al canal en Telegram

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

Mostrar más

📈 Análisis del canal de Telegram C# (C Sharp) programming

El canal C# (C Sharp) programming (@csharp_ci) en el segmento lingüístico de Ruso es un actor destacado. Actualmente la comunidad reúne a 18 319 suscriptores, ocupando la posición 7 281 en la categoría Tecnologías y Aplicaciones y el puesto 36 747 en la región Rusia.

📊 Métricas de audiencia y dinámica

Desde su creación el невідомо, el proyecto ha mostrado un crecimiento acelerado, reuniendo a 18 319 suscriptores.

Según los últimos datos del 23 junio, 2026, el canal mantiene una actividad estable. En los últimos 30 días la variación de miembros fue de 27, y en las últimas 24 horas de 4, conservando un alto alcance.

  • Estado de verificación: No verificado
  • Tasa de interacción (ER): El promedio de interacción de la audiencia es 17.49%. Durante las primeras 24 horas tras publicar, el contenido suele obtener 7.97% de reacciones respecto al total de suscriptores.
  • Alcance de las publicaciones: Cada publicación recibe en promedio 3 204 visualizaciones. En el primer día suele acumular 1 460 visualizaciones.
  • Reacciones e interacción: La audiencia responde de forma activa: el promedio de reacciones por publicación es 0.
  • Intereses temáticos: El contenido se centra en temas clave como .net, api, логика, архитектура, string.

📝 Descripción y política de contenido

El autor describe el recurso como un espacio para expresar opiniones subjetivas:
По всем вопросам- @notxxx1 Реестр РКН: https://clck.ru/3Fk3kb #VRHSZ

Gracias a la alta frecuencia de actualizaciones (últimos datos recibidos el 24 junio, 2026), el canal mantiene la vigencia y un amplio alcance. La analítica demuestra que la audiencia interactúa activamente con el contenido, lo que lo convierte en un punto de referencia dentro de la categoría Tecnologías y Aplicaciones.

18 319
Suscriptores
+424 horas
+177 días
+2730 días
Atraer Suscriptores
junio '26
junio '26
+136
en 3 canales
mayo '26
+131
en 2 canales
Get PRO
abril '26
+76
en 1 canales
Get PRO
marzo '26
+96
en 23 canales
Get PRO
febrero '26
+112
en 45 canales
Get PRO
enero '26
+398
en 132 canales
Get PRO
diciembre '25
+117
en 5 canales
Get PRO
noviembre '25
+318
en 44 canales
Get PRO
octubre '25
+84
en 14 canales
Get PRO
septiembre '25
+162
en 37 canales
Get PRO
agosto '25
+223
en 9 canales
Get PRO
julio '25
+418
en 77 canales
Get PRO
junio '25
+293
en 71 canales
Get PRO
mayo '25
+238
en 1 canales
Get PRO
abril '25
+191
en 28 canales
Get PRO
marzo '25
+185
en 2 canales
Get PRO
febrero '25
+210
en 42 canales
Get PRO
enero '25
+200
en 46 canales
Get PRO
diciembre '24
+416
en 56 canales
Get PRO
noviembre '24
+1 096
en 187 canales
Get PRO
octubre '24
+1 800
en 73 canales
Get PRO
septiembre '24
+635
en 199 canales
Get PRO
agosto '24
+662
en 44 canales
Get PRO
julio '24
+665
en 53 canales
Get PRO
junio '24
+1 168
en 60 canales
Get PRO
mayo '24
+565
en 39 canales
Get PRO
abril '24
+569
en 45 canales
Get PRO
marzo '24
+709
en 21 canales
Get PRO
febrero '24
+1 055
en 2 canales
Get PRO
enero '24
+1 376
en 46 canales
Get PRO
diciembre '23
+608
en 44 canales
Get PRO
noviembre '23
+1 091
en 30 canales
Get PRO
octubre '23
+332
en 19 canales
Get PRO
septiembre '23
+352
en 0 canales
Get PRO
agosto '23
+1 949
en 0 canales
Get PRO
julio '23
+507
en 0 canales
Get PRO
junio '23
+1 238
en 0 canales
Get PRO
mayo '23
+1 891
en 0 canales
Get PRO
abril '23
+3 025
en 0 canales
Get PRO
marzo '23
+1 858
en 0 canales
Get PRO
febrero '23
+85
en 0 canales
Get PRO
enero '23
+113
en 0 canales
Get PRO
diciembre '22
+171
en 0 canales
Get PRO
noviembre '22
+226
en 0 canales
Get PRO
octubre '22
+197
en 0 canales
Get PRO
septiembre '22
+170
en 0 canales
Get PRO
agosto '22
+756
en 0 canales
Get PRO
julio '22
+82
en 0 canales
Get PRO
junio '22
+152
en 0 canales
Get PRO
mayo '22
+344
en 0 canales
Get PRO
abril '22
+256
en 0 canales
Get PRO
marzo '22
+53
en 0 canales
Get PRO
febrero '22
+45
en 0 canales
Get PRO
enero '22
+97
en 0 canales
Get PRO
diciembre '21
+45
en 0 canales
Get PRO
noviembre '21
+1 110
en 0 canales
Fecha
Crecimiento de Suscriptores
Menciones
Canales
24 junio+1
23 junio+11
22 junio+10
21 junio+4
20 junio+3
19 junio+4
18 junio+11
17 junio+5
16 junio+3
15 junio+5
14 junio+4
13 junio+8
12 junio+6
11 junio+8
10 junio+10
09 junio0
08 junio0
07 junio+2
06 junio+3
05 junio+12
04 junio+10
03 junio+8
02 junio+4
01 junio+4
Publicaciones del Canal
Совет по .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
Sin texto...
3 892