C# Portal | Программирование
13.2K subscribers
1.43K photos
132 videos
31 files
1.07K links
Присоединяйтесь к нашему каналу и погрузитесь в мир для C#-разработчика

Сотрудничество, реклама: @devmangx

Работаем с @Spiral_Yuri

РКН: https://clck.ru/3FocB6
Download Telegram
Оператор присваивания с проверкой на null ??=

Оператор ??= появился в C# 8.0 и работает по принципу оператора объединения с null ??.

Он присваивает правому операнду значение только в том случае, если левый операнд равен null.

string? name = null;

name ??= "Developer";

Console.WriteLine(name); // Developer


Если переменная уже содержит значение, оно останется без изменений:
string? name = "Alex";

name ??= "Developer";

Console.WriteLine(name); // Alex


Оператор ??= позволяет сократить код и удобно задавать значения по умолчанию, если переменная ещё не инициализирована.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🙏5❤1🔥1🍾1
Упрощаем работу с коллекциями в C# с помощью Collection Expressions

В C# 12 появились Collection Expressions — выражения коллекций, которые позволяют создавать коллекции с помощью простого и лаконичного синтаксиса [].

Вместо длинной записи можно использовать квадратные скобки, делая код чище и понятнее.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍3👎1👏1🍾1
7 ошибок с async/await, которые допускают даже Senior .NET-разработчики

После нескольких лет работы с production-приложениями на .NET становится понятно: написать асинхронный код несложно. Гораздо сложнее сделать так, чтобы он правильно работал под высокой нагрузкой.

Вот 7 ошибок, которых стоит избегать.

1. Блокировка асинхронного кода через .Result или .Wait(). Вызовы .Result и .Wait() блокируют текущий поток в ожидании завершения асинхронной операции. В ASP.NET Core это может привести к исчерпанию потоков Thread Pool (Thread Pool Starvation) и снижению пропускной способности приложения.

Как правильно: использовать await на протяжении всей цепочки вызовов.

2. Использование async void вместо async Task. Метод с async void нельзя ожидать через await, а его исключения нельзя перехватить вызывающим кодом с помощью стандартного механизма обработки исключений на основе Task.

Как правильно: возвращать Task или Task<T>. async void стоит использовать только для обработчиков событий, которым необходим такой формат.

3. Забывать await при вызове асинхронного метода. Если вызвать асинхронный метод без await и не отслеживать возвращённый Task, можно потерять исключения или получить операцию, которая продолжит выполняться за пределами ожидаемого жизненного цикла.

Как правильно: использовать await или явно управлять жизненным циклом задачи.

4. Выполнять независимые операции последовательно. Представим, что нужно сделать два независимых API-запроса. При последовательном выполнении второй начнётся только после завершения первого.

Как правильно: запускать обе операции и ожидать их завершения вместе с помощью Task.WhenAll. Это подходит, если операции действительно независимы и безопасны для параллельного выполнения.

5. Игнорировать CancellationToken. Если не передавать сигнал отмены дальше по цепочке вызовов, ресурсоёмкие запросы к базе данных или HTTP-запросы могут продолжать выполняться даже после отключения клиента.

Как правильно: принимать CancellationToken и передавать его через всю цепочку асинхронных вызовов.

6. Считать, что async означает параллельное выполнение. async/await автоматически не создаёт новые потоки и не ускоряет вычислительно сложные операции. Асинхронность в первую очередь позволяет не блокировать поток во время ожидания.

Как правильно: использовать async для операций ввода-вывода (I/O-bound). Task.Run может подойти для некоторых CPU-bound задач, но его применение в обработчиках серверных запросов требует осторожности.

7. Использовать Task.WhenAll без ограничения параллелизма. Одновременный запуск тысяч запросов к базе данных или HTTP-запросов может исчерпать пулы соединений, перегрузить внешние сервисы и увеличить задержки.

Как правильно: ограничивать количество одновременно выполняемых операций с помощью SemaphoreSlim, Parallel.ForEachAsync или других механизмов управления конкурентностью.

Главное: async/await — это не просто способ повысить производительность. Это инструмент управления ресурсами, параллелизмом, отменой операций и обработкой ошибок.

Метод может быть асинхронным, но при этом замедлять приложение, снижать его надёжность и усложнять отладку.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8😐4🍾1
Автоматическая регистрация зависимостей в ASP.NET Core с помощью Scrutor

Scrutor расширяет встроенный DI-контейнер ASP.NET Core и позволяет автоматически находить и регистрировать сервисы по заданным соглашениям (conventions).

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🍾1
BenchmarkDotNet в .NET Single-File приложениях

Бенчмарки — отличный сценарий использования одnofайловых приложений (Single-File Apps) в .NET.

В примере показано, как использовать BenchmarkDotNet в таком приложении. Самые важные настройки находятся в начале кода, а также стоит обратить внимание на атрибут [InProcess].

Автор делится примером реализации и готов отправить исходный код тем, кто заинтересуется им в комментариях.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🍾4
Please open Telegram to view this post
VIEW IN TELEGRAM
😁12👨‍💻3🍾1👀1
CQRS без Event Sourcing: используйте сильные стороны EF Core и Dapper

Чтобы получить преимущества CQRS, не обязательно внедрять полноценный Event Sourcing.

Достаточно разделить модели записи и чтения:

• EF Core — для бизнес-правил, доменных агрегатов и операций записи.
• Dapper — для быстрых SQL-запросов и чтения данных.

Такой подход позволяет сохранить бизнес-логику в одном месте, а запросы к данным сделать проще и производительнее.

Прагматично, чисто и без лишней сложности.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🍾2
Message Brokers гарантируют At-Least-Once, а не Exactly-Once

Что произойдёт, если ваш consumer обработает одно и то же событие дважды? Сможет ли система корректно справиться с повторной доставкой?

Брокеры сообщений часто используют семантику доставки At-Least-Once — событие будет доставлено как минимум один раз, но возможны дубликаты.

Поэтому обработчики событий должны быть идемпотентными (Idempotent): повторная обработка одного и того же события не должна приводить к повторным побочным эффектам.

Разберём, как проектировать идемпотентные обработчики событий и защищать систему от дублирующей обработки.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2🍾1
Зачем обрабатывать элементы по одному, если процессор может обрабатывать сразу 8?

В C# есть инструменты для использования SIMD (Single Instruction, Multiple Data) — технологии, которая позволяет выполнять одну операцию одновременно над несколькими элементами данных.

Для этого можно использовать Vector<T> и Hardware Intrinsics.

Что они дают?

Vector<T> — позволяет обрабатывать несколько значений одного типа за одну операцию.

Hardware Intrinsics — дают более низкоуровневый доступ к SIMD-инструкциям процессора, например AVX2 и AVX-512.

SIMD — ускоряет вычисления над массивами чисел, обработку изображений, математические операции и другие задачи с однотипными данными.

И всё это доступно прямо в C# — без необходимости переходить на C++.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔5👍2🔥2🍾1
Учитывают ли ваши C#-структуры кэш-линии процессора?

Неправильное расположение полей структуры приводит к неэффективному использованию памяти и дополнительным затратам на доступ к кэш-линии процессора.

Используйте [StructLayout(LayoutKind.Sequential, Pack = 1)], чтобы контролировать размещение полей в памяти.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2🤔2👏1🍾1
Каждый раз, когда асинхронный метод возвращает Task<T>, при завершении операции выделяется память в куче.

Если ваш метод часто завершается синхронно (например, при попадании в кеш), используйте ValueTask<T>.

Это поможет избежать миллионов лишних выделений памяти (heap allocations).


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🍾4
Запускаете несколько реплик фонового воркера в Kubernetes?

Локальные блокировки (lock / Monitor) не работают между разными контейнерами.

Используйте распределённые блокировки Redis (Redis Distributed Locks) с fencing-токенами, чтобы предотвратить состояния гонки (race conditions) и избежать одновременного выполнения одной задачи несколькими воркерами.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3❤‍🔥2🍾2
Получаете случайные ошибки 502 Bad Gateway во время деплоя в Kubernetes?

Вероятно, ваше .NET-приложение завершает работу до того, как входящий трафик будет перенаправлен на другие экземпляры.

Разберитесь, как правильно настроить graceful shutdown и health probes в ASP.NET Core.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🍾3
Task.WhenAll vs Parallel.ForEachAsync в .NET: что выбрать?

Оба инструмента позволяют выполнять независимые операции конкурентно, но отличаются способом управления параллельностью и ресурсами.

Правильный выбор зависит от типа нагрузки.

1. Task.WhenAll. Лучше всего подходит для известного набора независимых асинхронных операций, особенно связанных с вводом-выводом (I/O-bound).

var usersTask = GetUsersAsync();
var ordersTask = GetOrdersAsync();

await Task.WhenAll(usersTask, ordersTask);


• Идеален для нескольких независимых запросов к API или базе данных.
• Не имеет встроенного ограничения количества одновременно выполняемых операций.
• Не создаёт потоки автоматически.

2. Parallel.ForEachAsync. Лучше всего подходит для обработки коллекций с контролируемым уровнем параллельности.

await Parallel.ForEachAsync(
orders,
new ParallelOptions
{
MaxDegreeOfParallelism = 10
},
async (order, ct) =>
await ProcessOrderAsync(order, ct));


• Поддерживает I/O-bound и CPU-bound нагрузки.
• Имеет встроенный механизм ограничения параллельности.
• Полезен при обработке больших коллекций и выполнении внешних API-запросов.

3. Что выбрать?

• CPU-bound: предпочитайте Parallel.ForEach для синхронных операций с высокой вычислительной нагрузкой.
• I/O-bound: используйте Task.WhenAll для нескольких независимых операций или Parallel.ForEachAsync, если необходимо ограничить параллельность.

Внешние API: подходят оба варианта, но Parallel.ForEachAsync часто удобнее для обработки сотен запросов без чрезмерной нагрузки на внешние сервисы.

Важное напоминание об EF Core: DbContext не является потокобезопасным (thread-safe).

Никогда не выполняйте параллельные запросы через один и тот же экземпляр DbContext. Используйте отдельные экземпляры или выполняйте запросы последовательно.

Главное:

• Task.WhenAll ожидает завершения нескольких задач без встроенного ограничения параллельности.

• Parallel.ForEachAsync обрабатывает коллекции с ограниченным уровнем параллельности.

И помните: по умолчанию Parallel.ForEachAsync допускает количество одновременно выполняемых операций, равное Environment.ProcessorCount. Настраивайте MaxDegreeOfParallelism с учётом особенностей вашей нагрузки.

Какой из этих инструментов вы чаще используете в production-приложениях на .NET?

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🍾1
CQRS ≠ MediatR.

Перестаньте смешивать CQRS и MediatR.

То, что вы используете MediatR, не означает, что у вас реализован CQRS.

CQRS — это про разделение операций чтения и записи, часто с разными моделями, стратегиями хранения и иногда с разными хранилищами данных.

MediatR — это библиотека для диспетчеризации сообщений внутри процесса. Она удобна для организации команд и запросов, но это не фреймворк CQRS.

Можно использовать MediatR в архитектуре, построенной по CQRS.

Но не стоит считать, что одно автоматически означает другое.

Сначала разберитесь в различии, прежде чем внедрять паттерн или инструмент.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6👏1🍾1