C# 1001 notes
6.53K subscribers
320 photos
9 videos
2 files
306 links
Регулярные короткие заметки по C# и .NET.

Просто о сложном для каждого.

admin - @haarrp
加入频道
🗂️ Entity Framework Core — ORM нового поколения для работы с БД. Этот инструмент от Microsoft кардинально меняет подход к взаимодействию с реляционными и NoSQL базами данных.

Для работы инструмент предоставляет мощный слой абстракции, позволяя работать с данными как с объектами, автоматизируя CRUD-операции и миграции схемы. EF Core поддерживает широкий пласт СУБД: от классических SQL Server и PostgreSQL до документоориентированной Cosmos DB. Для SQLite-разработчиков есть отдельный оптимизированный провайдер Microsoft.Data.Sqlite.

🤖 GitHub
Forwarded from C++ Academy
This media is not supported in your browser
VIEW IN TELEGRAM
🚀 Разработчик показал движок на C, который работает в 14 раз быстрее Unity в браузере — и теперь доступна онлайн-демо.

Многие не поверили в заявленную разницу в производительности, поэтому он выложил демо в открытый доступ. Сравнение проводится с реальным Unity-проектом, выложенным на GitHub.

🛠️ C-движок демонстрирует:
• Существенно более высокую FPS в браузере
• Минимальную просадку при рендеринге
• Низкий overhead по сравнению с WebAssembly-сборкой Unity

💬 Автор пока не решил, выкладывать ли исходники C-версии — рассматривает вариант лицензии CC (non-commercial).



🔗 Демо: https://cgamedev.com/
🔗 Код: https://github.com/gabrieldechichi/unity_webglperftest


@cpluspluc
👶 Junior developer пишет: notInactive, tempBool
🧑 Middle developer пишет: hadSubscriptionOnceUponATime
🧓 Senior developer пишет: hasSubscription, isActive

🤔 Почему так происходит?

👉 Потому что булевы переменные — просты по сути, но часто используются неинтуитивно, что делает код запутанным и плохо поддерживаемым.

Вот 7 простых правил, как работать с булевыми значениями в C# и резко улучшить читаемость вашего кода:

1. 🚫 Избегайте двойных отрицаний

user.IsNotActive
user.IsActive

user.HasNoDept
user.HasDept

creditCard.IsNotExpired
creditCard.IsExpired

Двойные отрицания сбивают с толку и делают логику трудной для чтения.

2. Используйте понятные префиксы

Is: user.IsActive
Has: user.HasDept
Should: order.ShouldBeCanceled

Префиксы мгновенно раскрывают назначение переменной.

3. 🔤 Предпочитайте единственное число

areUsers
hasUsers

Единственное число делает значение ясным и конкретным.

4. 🎯 Используйте прилагательные для описания состояния

CancelOrder
IsOrderCanceled

Булевы переменные должны описывать состояние, а не действие.

5. Используйте настоящее время

card.WasExpired
card.IsExpired

Текущие состояния — проще и универсальнее в коде.

6. 🔁 Принцип раннего возврата (Return Early)

Пишите условия и булевы проверки так, чтобы код читался сверху вниз, как история — коротко, логично, понятно.

7. ⚠️ Не передавайте булевы параметры

SendEmail(true)
SendEmail(SendMode.Immediate)

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

📌 Вывод:
Хорошие имена булевых переменных — это не косметика, а основа читаемости и архитектурной чистоты.
Хорошо названный isActive понятнее, чем любые комментарии и тесты.

💬 А ты какие правила используешь для наименования булевых переменных? Делись в комментариях 👇
[Best Practice] Явная конфигурация приложений ASP.NET Core

В большинстве существующих .NET-проектов конфигурация оказывается неочевидной, хотя в экосистеме .NET уже есть всё необходимое для ясной и понятной настройки ASP.NET Core веб-приложений с DI.

Как устроена конфигурация ASP.NET Core
- Источники (в порядке приоритета):
1. Host configuration
2. appsettings.json
3. appsettings.{Environment}.json
4. User secrets (в среде Development)
5. переменные окружения
6. аргументы командной строки
При дублировании ключей побеждает источник, указанный позже :contentReference[oaicite:0]{index=0}.

Выбор способа доступа к настройкам
- Через IConfiguration
- Через IOptions<T> / IOptionsMonitor<T>
Предпочтение паттерну Options даёт типобезопасный доступ к группам настроек и делает их использование очевидным :contentReference[oaicite:1]{index=1}.

Где хранить разные параметры
- `appsettings.json` — общие настройки (без секретов).
- `appsettings.{Environment}.json` — переопределения для конкретного окружения.
- User Secrets (`secrets.json`) — локальные секреты (пароли, ключи API); не используйте appsettings.json или appsettings.Development.json для них :contentReference[oaicite:2]{index=2}.
- Azure Key Vault — производственные секреты через builder.Configuration.AddAzureKeyVault(...) :contentReference[oaicite:3]{index=3}.
- Переменные окружения и CLI — для срочных или разовых переопределений.

Рекомендации по внедрению
- Избегайте дублирования ключей — задавайте значение только там, где оно действительно меняется.
- В README укажите, что разработчикам нужно получить secrets.json из менеджера паролей и настроить User Secrets.
- В IaC-скриптах чётко обозначьте необходимые секреты и параметры для каждого окружения.

Преимущества подхода
- Новым участникам проекта легко разобраться в конфигурации — каждый ключ встречается в одном месте.
- Возрастает поддерживаемость и прозрачность настроек.
- Упрощается автоматизация развёртывания (IaC): сразу видно, какие секреты и параметры требуются.

Читать
In-memory кэш в .NET: плюсы и минусы

Плюсы:
• Очень быстрый
• Легко реализуется
• Не требует внешних зависимостей

⚠️ Минусы:
• Данные теряются при перезапуске
• Работает только в пределах одного инстанса
• Нет шаринга между приложениями

📌 Нужно больше — переходите на распределённый кэш.

Redis — топовый выбор, и в .NET есть два стабильных клиента:
StackExchange.Redis
Microsoft.Extensions.Caching.StackExchangeRedis

📘 Хотите понять, когда и как использовать оба подхода?
Вот полный гайд:
https://milanjovanovic.tech/blog/caching-in-aspnetcore-improving-application-performance
🖥 Задача «Перехват исключений и потеря контекста» (C# 12)

Дан код:


using System;
using System.Runtime.ExceptionServices;

class Program
{
static void Main()
{
try
{
Console.WriteLine(Foo());
}
catch (Exception ex)
{
Console.WriteLine($"Catch: {ex.Message}");
}
}

static int Foo()
{
try
{
return Bar();
}
finally
{
throw new Exception("finally in Foo");
}
}

static int Bar()
{
try
{
throw new Exception("error in Bar");
}
finally
{
Console.WriteLine("Bar finally executed");
}
}
}


Вопросы:
• Что напечатает программа и почему?
• В каком порядке выполняются блоки try / finally?
• Какая из двух ошибок дойдёт до catch, а какая потеряется?
• Как изменить код, чтобы не терять информацию о первой ошибке?

Разбор:
• Bar бросает Exception("error in Bar"). Прежде чем исключение покинет метод, выполняется его finally, выводя Bar finally executed.
• Управление переходит в Foo. Там срабатывает finally, который бросает новое исключение ("finally in Foo").
• По правилам CLR новое исключение из finally заменяет текущее. Первая ошибка пропадает.
• В Main перехватывается только «finally in Foo».

Консольный вывод:

Bar finally executed
Catch: finally in Foo

Как сохранить обе ошибки

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


finally
{
var pending = new Exception("finally in Foo");
throw new AggregateException("Foo failed", pending);
}


или переслать исходное исключение через ExceptionDispatchInfo:

Exception pending = null;

static int Foo()
{
try
{
return Bar();
}
catch (Exception ex)
{
pending = ex; // сохраняем первую причину
throw;
}
finally
{
if (pending != null)
ExceptionDispatchInfo.Capture(pending).Throw();
}
}```

Так ни одна ошибка не будет потеряна, а отладка станет нагляднее.
Please open Telegram to view this post
VIEW IN TELEGRAM
🖥Быстрая сортировка (QuickSort) с использованием рекурсии

Проблема: cортировка больших массивов может быть неэффективной при использовании простых алгоритмов, таких как сортировка пузырьком или вставками.

Решение: Автор в книге Algorithms and Data Structures for OOP With C# демонстрирует реализацию QuickSort — одного из самых эффективных алгоритмов сортировки на практике, с рекурсивным разбиением массива.

Пример кода:

public class QuickSortExample
{
public void QuickSort(int[] arr, int low, int high)
{
if (low < high)
{
int pi = Partition(arr, low, high);

QuickSort(arr, low, pi - 1);
QuickSort(arr, pi + 1, high);
}
}

private int Partition(int[] arr, int low, int high)
{
int pivot = arr[high];
int i = (low - 1);

for (int j = low; j < high; j++)
{
if (arr[j] < pivot)
{
i++;
(arr[i], arr[j]) = (arr[j], arr[i]);
}
}

(arr[i + 1], arr[high]) = (arr[high], arr[i + 1]);
return i + 1;
}
}


Преимущества:
— Быстрая сортировка даже больших наборов данных
— Средняя сложность O(n log n)
— Эффективное использование памяти за счет рекурсии
Please open Telegram to view this post
VIEW IN TELEGRAM
Трюк: добавление элементов в словарь со списками через `??=`


var dict = new Dictionary<string, List<int>>();
string key = "numbers";
int item = 42;

// Обычный способ:
if (!dict.TryGetValue(key, out var list))
{
list = new List<int>();
dict[key] = list;
}
list.Add(item);

// Короткий трюк:
(dict[key] ??= new List<int>()).Add(item);


Разбор:

- При обращении к dict[key] отсутствующий ключ вернёт default(List<int>), то есть null.

- Оператор ??= проверяет левую часть на null и, если она null, присваивает справа новое значение.

- В нашем случае, если dict[key] был null, создаётся новый List<int> и сразу сохраняется в словаре.

- После этого метод .Add(item) вызывается уже на существующем списке.

- В результате за одну строчку мы и проверили наличие, и создали новый список при необходимости, и добавили элемент.
🎯 Using .NET Aspire With the Docker Publisher — практическое руководство от Milan Jovanović

.NET Aspire — это современный фреймворк от Microsoft для создания облачных микросервисов. В статье показано, как автоматически интегрировать Docker Compose в .NET‑приложение с помощью нового инструмента — Aspire Docker Publisher.

Что вы узнаете:

1. Как описать окружение прямо в C#:

builder.AddDockerComposeEnvironment("aspire-docker-demo");

var postgres = builder.AddPostgres("database").WithDataVolume();
var redis = builder.AddRedis("cache");

var webApi = builder.AddProject<Projects.Web_Api>("web-api")
.WithReference(postgres).WaitFor(postgres)
.WithReference(redis).WaitFor(redis);

builder.Build().Run();


2. Как опубликовать проект:

dotnet tool install --global aspire.cli --prerelease
aspire publish -o docker-compose-artifacts

После чего автоматически создаётся docker-compose.yml и .env.

3. Что входит в результат:
- Готовый docker-compose.yml со всеми зависимостями
- Поддержка портов, переменных среды, volume и сетей
- Полная инфраструктура, которую можно деплоить хоть на VPS

4. Как это работает на проде:
- Всё, что нужно: скопировать артефакты → docker compose up -d
- Можно легко обернуть через Nginx или Traefik, подключить SSL

🧠 Почему это удобно:
- Не нужно вручную писать YAML — всё в коде
- Повышается воспроизводимость и читаемость инфраструктуры
- Упрощает переход от локальной разработки к боевому деплою

🔗 Статья: www.milanjovanovic.tech/blog/using-dotnet-aspire-with-the-docker-publisher
🧠 .NET-задача для продвинутых: потокобезопасная очередь с приоритетами и отменой задач

Задача:

Реализуйте асинхронный планировщик задач с приоритетами и возможностью отмены. Требования:

1. Есть очередь задач (`Func<CancellationToken, Task>`), каждая с целочисленным приоритетом (0 — самый высокий).
2. Задачи выполняются параллельно, но не более N одновременно.
3. Если приходит задача с более высоким приоритетом, и нет свободных слотов, она может вытеснить задачу с самым низким приоритетом.
4. Вытеснённая задача должна быть отменена (через `CancellationToken`), и её ресурсы — корректно освобождены.
5. Код должен быть потокобезопасным и устойчивым к гонкам.

Дополнительно:

- Используйте PriorityQueue, SemaphoreSlim, CancellationTokenSource
- Не допускайте deadlock’ов
- Обеспечьте корректное завершение планировщика по команде StopAsync()

Пример API:

public class PriorityTaskScheduler
{
public PriorityTaskScheduler(int maxParallelism);

public Task EnqueueAsync(Func<CancellationToken, Task> task, int priority);

public Task StopAsync();
}


@csharp_1001_notes
👩‍💻 🎯 Открытый урок «Асинхронность в C#: за гранью await. Паттерны, ошибки и оптимизация для профессионалов».

🗓 17 июля в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «C# Developer. Professional».

Что будет на вебинаре:
✔️ Разбор сложных сценариев: цепочки задач, параллельный async, комбинирование с параллелизмом (Task, Parallel, async).
✔️ Распространенные ошибки (deadlocks, async void, контексты синхронизации) и как их точно избегать.
✔️ Паттерны: CancellationTokens, ValueTask, IAsyncDisposable, кастомные awaiterы (обзорно).
✔️ Когда и как измерять производительность async кода, что может стать узким местом.
✔️ Best practices для реальных высоконагруженных сценариев.

Кому будет полезно:
- Разработчикам C# с опытом (Mid+/Senior), которые активно используют async/await.
- Тем, кто сталкивался с непонятными блокировками или проблемами производительности в асинхронном коде.
- Тем, кто хочет писать более надежный и эффективный асинхронный код.

🔗 Ссылка на регистрацию: https://otus.pw/SIHc/
⚠️ Как вы обрабатываете ошибки в C#?

Многие используют исключения для управления потоком и быстрого фейла. Но в C# метод не сообщает, какие именно исключения он может выбросить — это не видно в сигнатуре.

🔍 Мой подход:
Исключения — только для исключительных ситуаций.

Если метод может ожидаемо провалиться, пусть это будет явно.

Используйте Result-паттерн:

— Метод возвращает Result<T> вместо выбрасывания исключения
— Caller обязан проверить IsSuccess и обработать ошибку
— Код становится предсказуемее и легче тестируется
— Дополнительно: пропускная способность может быть выше, чем при throw/catch

Пример:

Result<User> result = userService.FindById(id);
if (!result.IsSuccess)
return Error(result.Error);


Подробнее
🖥 Топ-10 ошибок .NET‑разработчиков

Если ты пишешь на .NET — возможно, ты хотя бы раз совершал одну из этих архитектурных (или просто утомительных) ошибок. Вот список анти-паттернов.

1. Blazor вместо React
Переизобретать веб на C# ради UI? Ты не Google. Если нужен зрелый фронт, бери то, что уже доказало свою масштабируемость.

2. Serverless Azure Functions на одном App Service Plan
"О, это же серверлесс!" — пока не замечаешь, что всё сидит на одном сервисе. Масштабируемость мнимая, расходы настоящие.

3. Самодельный MVC поверх Minimal APIs
Зачем ты пишешь свою обвязку маршрутов, контроллеров и зависимостей, если MVC уже всё это умеет?

4. Хранимки для CRUD
Хранимки — не антипаттерн, но когда их 200 штук для банального Insert/Update/Delete — это просто ORM с человеческим лицом, только сложнее.

5. "Юнит-тесты", которым нужен деплой БД
Это не юнит-тесты. Это интеграционные, а если быть честным — "fragile-тесты".

6. AutoMapper
В теории красиво, в проде — «а почему здесь null?». Иногда проще написать руками и понять, что происходит.

7. Игнор Microsoft Orleans
Если делаешь real-time, pub/sub, воркеры — Orleans стоит хотя бы попробовать. Он закрывает много боли, которую ты иначе сам будешь собирать по кускам.

8. Начинать с "микросервисов"
Монолит — не зло. Зло — это 12 проектов в solution, которые общаются через HTTP и собираются 10 минут.

9. gRPC для фронта
Если у тебя браузер → JS → gRPC → base64 → JSON — просто остановись. Возьми REST или GraphQL и живи спокойно.

10. Razor Pages вместо MVC
Да, они проще на старте. Но когда проект растёт, ты захочешь нормальное разделение по слоям, маршрутам и структуре.

🧠 Вывод: не все возможности .NET надо использовать. Иногда сила — в простоте.

C чем согласны ? Что добавили бы ?


#dotnet #csharp #webdev #архитектура #программирование
Please open Telegram to view this post
VIEW IN TELEGRAM
🔐 Блог DevelopersVoice выпустил отличный гайд по **10 главным уязвимостям веб‑приложений с примерами на .NET.

Что внутри:
• Инъекции и XSS
• Ошибки аутентификации
• Уязвимые зависимости
• SSRF и плохая конфигурация
• Проблемы с логированием и безопасным дизайном

📌 Всё с практическими советами: как обнаружить, как исправить, как не допустить.

Полный гайд тут: https://developersvoice.com/blog/secure-coding/owasp-top-ten

#OWASP #SecureCoding #DotNet #WebSecurity #DevTips
📌 PolySharp — удобный способ использовать новые фичи C# на старых версиях .NET. Этот NuGet-пакет работает как source-генератор, автоматически подбирая нужные полифиллы в зависимости от целевой платформы. Для работы достаточно добавить ссылку на PolySharp, установить последнюю версию C# и можно писать современный код даже для .NET Framework или UWP.

Инструмент обладает умной генерацией только необходимых типов. Например, если компилятору C# 13 нужен [IsExternalInit] для init-only свойств, PolySharp создаст его за кулисами. При этом он не трогает фичи, требующие поддержки рантайма, но покрывает огромный пласт синтаксических улучшений — от nullable-аннотаций до интерполированных строковых обработчиков.

🤖 GitHub

@csharp_ci
Какое исключение выдается, если протокол, поддерживаемый префиксом URI, недействителен?
Anonymous Quiz
11%
URLNotFound
30%
NotSupportedException
48%
UriFormatException
11%
URLSourceNotFound
🔥 .NET Aspire: как упростить Service Discovery в микросервисах

Вместо ручной настройки адресов и портов, фреймворк .NET Aspire позволяет упростить обнаружение сервисов с помощью декларативного подхода.

🔹 Конфигурация через .AddProject() и .WithReference()
🔹 Сервисы автоматически "обнаруживаются" и подключаются
🔹 Поддержка локальной разработки, контейнеров и облака
🔹 Логи, метрики, health checks — встроены

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

🧠 Полный разбор — в блоге:
[how-dotnet-aspire-simplifies-service-discovery](https://www.milanjovanovic.tech/blog/how-dotnet-aspire-simplifies-service-discovery)

Подпишись, чтобы не пропускать важные новинки .NET и облачной разработки.
😱💻  Хотите прокачать архитектурные скилы и стать востребованным fullstack-разработчиком?

Пройдите вступительный тест и получите бесплатные уроки курса «C# ASP.NET Core разработчик» от OTUS!

👉 Пройти тест: https://otus.pw/gmbX/

🚀 Зарядите карьеру: увеличьте доход, берите сложные проекты и работайте с современным стеком!
Всего за 6 месяцев вы научитесь:
• Разрабатывать веб-приложения на ASP.NET Core, рассматривая ASP.NET подробно, со всеми его механизмами
• Создавать различные технологии межсервисного взаимодействия + реалтайм с клиентским приложением
•Интегрировать фронтенд (ReactJS + JavaScript+Typescript) с бэкендом
• Тестировать приложения: интеграционные и нагрузочные тесты
• Автоматизировать процессы с CI/CD и Kubernetes
• Проектировать микросервисы и освоить event-driven архитектуру

🎁 Бонус: После теста — доступ к урокам!

👉 Проверьте свои силы прямо сейчас:  https://otus.pw/gmbX/?erid=2W5zFJd7K3v

Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
😈 Хитрая задачка на C# — замыкания и ловушка в цикле

Что выведет этот код?


var actions = new List<Action>();

for (int i = 0; i < 5; i++)
{
actions.Add(() => Console.WriteLine(i));
}

foreach (var action in actions)
{
action();
}


На первый взгляд кажется, что будет:


0
1
2
3
4

Но на самом деле вывод:


5
5
5
5
5


💡 Почему?
Все лямбды замкнулись на одну и ту же переменную i, и когда они выполняются — i уже стало 5.

Как исправить:


for (int i = 0; i < 5; i++)
{
int copy = i;
actions.Add(() => Console.WriteLine(copy));
}


Теперь всё работает как ожидается.

🧠 Замыкания в C# захватывают переменные, а не их значения! Аккуратнее с циклами и лямбдами.
Почти каждый разработчик допускал эту ошибку.

Что не так с этим кодом?

На первый взгляд всё кажется логичным:

🔸API-эндпоинт регистрации пользователя вызывает UserService
🔸UserService сохраняет пользователя в базу и вызывает EmailService
🔸EmailService через SmtpClient отправляет письмо

Но если присмотреться, метод SendWelcomeEmail объявлен как async void.

В чём проблема с async void?

Вот суть:
async void делает невозможным отлов исключений.

Если внутри SendEmailAsync() произойдёт исключение — catch его не перехватит.
Вместо этого приложение может тихо упасть или начать вести себя непредсказуемо.

Почему так происходит?

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

Правильный подход:

Всегда возвращай Task

Запомни: async void допустим только для обработчиков событий, где возвращаемый void обязателен.
Please open Telegram to view this post
VIEW IN TELEGRAM