C# Portal | Программирование
13.9K subscribers
1.15K photos
126 videos
29 files
928 links
Присоединяйтесь к нашему каналу и погрузитесь в мир для C#-разработчика

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

Менеджер: @Spiral_Yuri

РКН: https://clck.ru/3FocB6
Download Telegram
Два варианта N+1 в Entity Framework.

// ================================
// ПРОБЛЕМА N+1 — ВАРИАНТ 1
// Ленивый (Lazy) loading навигационного свойства
// (в EF Core Lazy Loading по умолчанию ВЫКЛЮЧЕН)
// ================================
//
// Что происходит:
// - 1 запрос загружает всех пользователей
// - Затем КАЖДОЕ обращение к user.Orders автоматически триггерит отдельный запрос
// - Итого = 1 + N запросов

var users = context.Users.ToList(); // Запрос #1

foreach (var user in users)
{
// Запрос #2, #3, #4... #N+1 (по одному на пользователя, триггерится автоматически)
var orders = user.Orders.ToList();
}


// ================================
// ПРОБЛЕМА N+1 — ВАРИАНТ 2
// Явный запрос к дочерним данным внутри цикла
// ================================
//
// Что происходит:
// - 1 запрос загружает всех пользователей
// - Затем КАЖДАЯ итерация выполняет отдельный запрос за заказами этого пользователя
// - Итого = 1 + N запросов

var users2 = context.Users.ToList(); // Запрос #1

foreach (var user in users2)
{
// Запрос #2, #3, #4... #N+1 (по одному на пользователя)
var orders = context.Orders
.Where(o => o.UserId == user.Id)
.ToList();
}


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
😐151👍1
Как заставить правила по данным применяться к КАЖДОМУ запросу, не копипастя одно и то же?

Global Query Filters в EF Core. Что важно знать:

1. Определяешь фильтр один раз в OnModelCreating, и EF Core сам добавляет WHERE ко всем запросам
2. Soft delete: HasQueryFilter(p => !p.IsDeleted) прячет удаленные записи без физического удаления
3. Multi-tenancy: ссылайся на поле DbContext для динамической фильтрации по tenant, запросы будут параметризованные
4. Named Filters (EF Core 10): можно задать НЕСКОЛЬКО фильтров на одну сущность и отключать их независимо
5. IgnoreQueryFilters(["SoftDelete"]): выключаешь ОДИН фильтр, остальные остаются активными
6. Паттерн с интерфейсом ISoftDelete: применяешь фильтры ко ВСЕМ сущностям, которые реализуют интерфейс, через expression trees
7. Индексируй колонки, по которым фильтруешь: IsDeleted и TenantId должны быть с индексами, потому что фильтры срабатывают на каждом запросе

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🤔21
Изменить (скорректировать) стандартный вид отладки для класса в Visual Studio

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
1❤‍🔥15👍7👀3👎2
Как маппить параметры query string в Minimal APIs?

(и простые, и сложные)

Query-параметры это опциональная часть URL, которую можно использовать, чтобы фильтровать или менять ответ в зависимости от переданных значений.

В Minimal APIs это делается довольно просто.

Minimal APIs поддерживают два типа query string параметров:

▪️Простые
▪️Сложные

Для простых параметров тебе достаточно, чтобы имена совпадали.

Для сложных типов нужно использовать атрибут [AsParameters], он замаппит свойства из запроса на класс.

Начиная с .NET 7 также появилась поддержка массивов в query-параметрах.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
5🤔1
Я много лет писал в проде и горизонтальный C#, и вертикальный. В итоге пришел вот к чему.

Горизонтальный стиль:

- Пишется быстрее
- Выглядит плотным и типа профессиональным
- Норм для совсем простых выражений
- После 100+ символов превращается в кашу
- Отлично прячет баги на виду (я лично пропустил 3 ошибки с неправильными аргументами в горизонтальном коде)

Вертикальный стиль:

- Занимает на пару секунд больше
- Выглядит слишком очевидно, прям до простоты
- Каждая операция читается отдельно, без напряга
- Дифы чище (одно изменение = одна строка в git)
- Ревью реально ускоряется

Мой дефолт: вертикально. Всегда. Но переключаюсь на горизонтальный, когда выражение реально простое: null-coalesce, базовый ternary, один метод в LINQ.

Правило, по которому решаю: если мне нужно провести глазами по строке горизонтально больше одного раза, значит надо делать вертикально.

Это не теория. Мы перелопатили 100+ .NET сервисов и сравнили время ревью PR до и после. В среднем ревью стало быстрее примерно на 30% после того, как ввели вертикальные стандарты форматирования.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍194
Lazy Loading по умолчанию выключен в Entity Framework (Core), так что тебе реально нужно целенаправленно включать и искать вот этот вариант N+1 ⬇️


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🥴3
Делегаты без накладных расходов в .NET 10

JIT в .NET 10 в некоторых случаях может полностью убрать стоимость абстракции при использовании делегатов.

Enumerable.Any может быть таким же эффективным, как вручную написанный цикл, который достает Span из List

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥71
Новый goto может появиться в C# 15

И ты будешь им пользоваться. Звучит стремно, да?

Но тут другая история.

Официально продвигаемое предложение для C# 15 добавляет labeled break и continue.

Это решает проблему, на которую все натыкаются в вложенных циклах.

И обычно мы решаем ее криво:
- Используем goto со странными метками где-то внизу цикла.
- Заводим флаги, которые “протекают” состоянием и прячут намерение.

Оба варианта делают код хуже для чтения и поддержки.

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

“Выйти из внешнего цикла.”
“Продолжить внешний цикл.”

И да, в других языках это уже есть:
→ Java, Kotlin, Rust, Go, Swift.

C# наконец догоняет. Эту фичу просили годами.

Сейчас она уже официально поддержана (championed), а значит, очень вероятно, что она действительно доедет.

И когда доедет, ты будешь ее использовать.

Не потому что это обязательно. А потому что так просто лучше.

Лично мне идея нравится. А тебе как эта фича в C#? 🚬

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍377👎6
C# Primary Constructor пример:

// ДО .NET 8 (традиционный подход с конструктором)
public class UserService
{
private readonly HospitalDBContext _dbContext;
private readonly ILogger<UserService> _logger;
private readonly IEmailService _emailService;

public UserService(
HospitalDBContext dbContext,
ILogger<UserService> logger,
IEmailService emailService)
{
_dbContext = dbContext;
_logger = logger;
_emailService = emailService;
}

// Методы бизнес-логики здесь...
}

// ПОСЛЕ .NET 8 (с использованием первичного конструктора)
public class UserService(
HospitalDBContext _dbContext,
ILogger<UserService> _logger,
IEmailService _emailService)
{
// Методы бизнес-логики здесь...
}


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13
CQS и CQRS часто путают.

CQS = Command Query Separation

Это про дизайн классов: метод либо меняет состояние , либо возвращает значение

CQRS = Command Query Responsibility Segregation

CQRS логически разделяет потоки чтения и потоки записи данных. Хотя это не взаимоисключающие вещи, CQRS можно воспринимать как более строгую версию CQS.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Это довольно приятное улучшение в Visual Studio.

Строки, состоящие только из пробелов, а также строки, где только скобки или знаки пунктуации, можно отображать меньшим шрифтом. Это немного возвращает вертикальное пространство и заодно снижает визуальный шум.

Эту настройку можно найти здесь:

Options -> Text Editor -> Advanced -> Compress Blank Lines

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍152🥴2🤔1
Только что словил неприятный нюанс в GetDirectories: когда ты явно передаешь EnumerationOptions, режим по умолчанию становится Simple.

На Windows по умолчанию используется Win32, когда запускаешь без этого, из-за чего старый синтаксис *.* начинает ломаться

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

Раньше я всегда использовал *.*, что, вероятно, на самом деле никогда не было корректным вариантом, но это работало и возвращало все папки при MatchType.Win32.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Blazor Minimum Project Templates v11.0.100-preview.1 получили поддержку таргета net11.0.

Если хочется стартовать с максимально чистого Blazor-шаблона, это как раз тот случай: без JS, без CSS-библиотек, с упором на чистый C#.

Можно ставить и пробовать уже сейчас для .NET 11 preview.

https://github.com/jsakamoto/BlazorMinimumTemplates

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍71
Чувак сделал намеренно уязвимое dotnet приложение 👀

Многие советы по безопасности в .NET остаются слишком абстрактными, пока ты не увидишь баг прямо в коде.

Поэтому этот проект, где всё намеренно сделано неправильно. Это намеренно уязвимое .NET-приложение, в котором собрано более 50 распространённых реальных ошибок, которые могут проскочить в обычный бизнес-код.

Ссылка - https://github.com/AlexGoOn/the-most-vulnerable-dotnet-app

Некоторые из включённых вещей:

Инъекционные атаки (SQL, command, template, LDAP, XML, logs)
Cross-Site Scripting (stored, reflected, в атрибутах, в SVG)
Небезопасная загрузка файлов (path traversal, Zip Slip, произвольная запись файлов)
Проблемы с криптографией (hashing, ECB, предсказуемый random)
Сериализация (XXE, XML bomb, binary, YAML)


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍3🤔1
8 лет опыта научили меня, что кидать исключения дорого.

Поэтому я начал делать так:

Раньше я бросал исключения, когда:

- пользователь не найден
- переданы невалидные параметры
- результат метода null

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

Но реальность такая: исключения отлично подходят для ошибок, с которыми ты не знаешь, что делать.

К счастью, большинство ошибок можно обработать, и вот как:

Паттерн Result.

Вместо того чтобы кидать исключения, методы возвращают объект Result, который явно показывает успех/провал и содержит либо успешное значение, либо информацию об ошибке.

Ещё стоит подумать о своей кастомной Error-модели, где ты будешь нормально, описательно расписывать все типы ошибок и использовать её внутри Result.

В своих проектах я люблю иметь две версии: не-дженерик (для void-методов) и дженерик (обёртка для конкретного результата). Так намерение кода читается сразу и без гаданий.

Я предпочитаю свою реализацию, но если тебе это не заходит, можно взять готовые библиотеки, например FluentResults.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🥴129👍7🤔3
Оператор LINQ FullJoin() появится в dotnet 11?

https://github.com/dotnet/runtime/issues/124787

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Когда стоит использовать паттерн Outbox?

Паттерн Outbox хранит исходящие сообщения в таблице базы данных, а потом фоновый процесс публикует их в брокер сообщений.

Зачем вообще заморачиваться?

Потому что в распределённых системах всё ломается: падают downstream-сервисы, отваливается сеть и т.д.

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

Outbox опирается на атомарные транзакции БД:

Сохранить состояние приложения
Сохранить сообщение в Outbox (либо оба шага, либо ни один)

А дальше фоновый процесс уже безопасно доставляет сообщение.

Важно помнить: Outbox решает надёжность публикации.

А на стороне консюмера всё равно нужна идемпотентность, чтобы нормально переживать ретраи.

Хочешь глубже?

Полная статья тут: https://milanjovanovic.tech/blog/implementing-the-outbox-pattern

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍43
C# records как value objects?

Records по задумке иммутабельные, и у них структурное сравнение (равенство по значениям).

А именно это обычно и нужно от value object.

Плюс сверху получаем pattern matching, короткий синтаксис и deconstruction.

public class Booking
{
public Address Address { get; init; }

public DateRange Period { get; init; }
}

public record Address(
string Street,
string City,
string State,
string Country,
string ZipCode);


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9😁1