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

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

Менеджер: @Spiral_Yuri

РКН: https://clck.ru/3FocB6
Download Telegram
API Gateway как RESTful-микросервис (версия 6.0)

Эта новая версия поддерживает .NET 10.
То есть теперь поддерживаются .NET 8/9/10.

hubs.li/Q041B9js0

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🤔2
This media is not supported in your browser
VIEW IN TELEGRAM
.NET 11 Preview 1 теперь доступен

Он приносит крупные улучшения в .NET Runtime, SDK, библиотеках, C#, ASP.NET Core, Blazor, .NET MAUI, новые анализаторы кода и не только. Полные release notes по ссылке. Можешь начинать пробовать уже сегодня.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍4🔥3
Если ты все еще сидишь на Swashbuckle в .NET 10, то вот что ты пропустил.

Swashbuckle нормально не обновлялся уже больше года. Поэтому Microsoft убрали его из шаблонов .NET 9+ и сделали свой встроенный вариант.

Замена: Microsoft.AspNetCore.OpenApi
Встроенный, легче, поддерживается и развивается.

UI: Scalar лучше, чем Swagger UI. Быстрее, выглядит приятнее, и темная тема есть из коробки.

Миграция реально на 5 минут: убираешь Swashbuckle, добавляешь builder.Services.AddOpenApi(), и готово.

Статья, которая поможет переехать: https://codewithmukesh.com/blog/dotnet-swagger-alternatives-openapi/

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20🔥4👎2
Асинхронное общение делает интеграционные тесты болью.

Ты шлёшь событие из одного модуля и... ждёшь?

Если опираться на Thread.sleep, твой CI/CD быстро начнёт страдать.

Не жди. Опрашивай.

Я использую паттерн “retry assertion” для проверки логики между модулями в модульных монолитах:

▪️Выполняем команду (модуль A)
▪️Поллим запрос (модуль B), пока Result != null
▪️Делаем ассерты по результату

Так флейковый, медленный тест превращается в детерминированный и быстрый.

Почитай про эту стратегию в статье

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍65
Статические абстрактные члены интерфейсов появились в C# 11. Основной мотивацией для них является поддержка обобщённой математики. Но они могут быть полезны и в других сценариях. Например, упростить код регистрации и использования типов разделов пользовательской конфигурации.

Например, вы хотите настроить клиент API в appSettings.json:

{

"GoogleAPI": {
"ApiKey": "…",
"OrganizationId": "…",

}
}


Хорошей практикой является связать раздел с отдельным типом:

public class GoogleOptions {
public string? ApiKey { get; init; }
public string? OrganizationId { get; init; }

}

// добавляем в Program.cs
builder.Configuration
.Configure<GoogleOptions>(
builder.Configuration.GetSection("GoogleAPI"));

// внедряем через DI
public class GoogleClient(
IOptions<GoogleOptions> options) {
// …
}


Упростим регистрацию через статические члены интерфейсов. Сначала определим интерфейс:

public interface IConfigOptions
{
static abstract string SectionName { get; }
}


Все реализации этого интерфейса должны иметь статическое свойство SectionName:

public class GoogleOptions: IConfigOptions {
public static string SectionName => "GoogleAPI";
// остальные члены
}


Добавим методы расширения для регистрации секций конфигурации:

public static class OptionsExtensions {
public static IHostApplicationBuilder
Configure<TOptions>(
this IHostApplicationBuilder builder)
where TOptions : class, IConfigOptions
{
var sect = builder.Configuration
.GetSection(TOptions.SectionName);
builder.Services.Configure<TOptions>(sect);
return builder;
}
}


Теперь можно регистрировать различные секции конфигурации так:

builder.Configure<GoogleOptions>()
.Configure<GitHubOptions>()
.Configure<WeatherOptions>()


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
11🥴5
Все еще держишь бизнес-логику в сервисах Application-слоя?

Это обычно знак, что доменная модель почти ничего не делает.

Если твои entity выглядят как голые DTO с полями, то у тебя анемичная доменная модель.

Как рефакторнуться в сторону behavior-driven дизайна:

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

В итоге код проще сопровождать, тестировать и расширять.

Пошаговый разбор рефакторинга

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
2
This media is not supported in your browser
VIEW IN TELEGRAM
Чувак собрал терминальный осциллограф для OPC UA на чистом C#, с рендерингом через брайлевские символы для 8x “пиксельного” разрешения.

Пара C# моментов, из-за которых это было прям кайфово:

1. Брайлевские символы как пиксельный движок

Вид осциллографа рисует осциллограммы через Unicode braille (U+2800–U+28FF). Каждая ячейка терминала становится матрицей точек 2×4, то есть дает в 8 раз больше разрешения по сравнению с обычным “символ-в-ячейке” рендерингом. Кастомный класс BrailleCanvas делает маппинг координат, алгоритм линий Брезенхэма для связных трасс, и композитинг слоев, чтобы сигнал всегда был поверх точек сетки. Около 270 строк, без графических зависимостей.

2. Terminal.Gui v2 для UI

TreeView, TableView, диалоги, меню, поддержка мыши. Осциллограф это кастомный View, который переопределяет OnDrawingContent и общается с ConsoleDriver для брайлевого рендера.

3. Подписки OPC UA вместо polling

Значения пушатся через события MonitoredItem.Notification, а в UI прокидываются через Application.Invoke(). Данные прилетают настолько быстро, насколько их публикует сервер.

4. Кроссплатформа

Windows/Linux/macOS без условной компиляции.

Исходники

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17
Поддержка LeftJoin в Entity Framework 10+ 🥳
Круто, что наконец-то это добавили в 10-й версии.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🤣3
Типы связей в EF Core, по-простому:

1:N -> HasOne().WithMany()
1:1 -> HasOne().WithOne().HasForeignKey<T>()
M:N -> HasMany().WithMany()
M:N с данными в связи -> отдельная join-entity (явная сущность для таблицы-связки)

И да: всегда задавай OnDelete явно. Дефолтный Cascade угробил больше баз, чем кривые запросы.

Полный гайд с примерами кода

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥84
Ты можешь комбинировать raw SQL-запросы и LINQ в EF Core.

Вот пример: сначала выполняем SELECT через SQL, а потом докидываем цепочкой OrderBy + Skip + Take уже через LINQ.

Вот код с картинки (как есть):

var startDate = new DateOnly(2023, 1, 1);

var ordersIn2023 = await dbContext
.Database
.SqlQuery<OrderSummary>(
$"SELECT * FROM OrderSummaries AS o WHERE o.CreatedOn >= {startDate}")
.OrderBy(o => o.Id)
.Skip(10)
.Take(5)
.ToListAsync();


-- SQL sent to the DB
SELECT s.Id, s.CustomerId, s.TotalPrice, s.CreatedOn
FROM (
SELECT * FROM OrderSummaries AS o WHERE o.CreatedOn >= @p0
) AS s
ORDER BY s.Id
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY


Обрати внимание: raw SQL-запрос здесь корректно параметризован, хотя на вид кажется, что мы используем интерполяцию строк.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥133👍3
Уикенд-проект: portless

Стабильные, “именованные” URL для локальной разработки.

▪️Ноль конфига
▪️Сабдомены для монореп
▪️Больше никакой рулетки с портами и протекания кук между сервисами
▪️Агентам можно давать надежный URL, вместо того чтобы угадывать порты

npm i -g portless


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
Улучшения по производительности таймзон в dotnet 11

В .NET 11 добавили кэш переходов таймзон по годам (per-year cache) для TimeZoneInfo, из-за чего конвертация времени стала заметно быстрее. Кэш хранит все переходы за конкретный год в UTC и убирает повторные lookup-и правил при каждом пересчете.

По бенчмаркам ускорение такое:

▪️Windows: примерно в 2.4–3.9 раза быстрее (например, ConvertTimeFromUtc с 48.0 нс до 12.2 нс)
▪️Linux: примерно в 1.6–4.7 раза быстрее (например, ConvertTimeToUtc с 63.8 нс до 13.6 нс)

Плюс новый путь с кэшем заменил старую “слоеную” логику конвертации (которую годами патчили) и заодно поправил несколько багов, где конвертация могла давать неправильный результат.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥32👍1
Твой GET-эндпоинт возвращает все строки из таблицы. На 100 записей ок. На 100К это уже катастрофа.

3 вещи, которые нужны любому list-эндпоинту в .NET:

- Пагинация (Skip + Take)
- Динамическая сортировка (по нескольким колонкам)
- Поиск (без учета регистра)

И все это одним SQL-запросом через EF Core 10.

https://codewithmukesh.com/blog/pagination-sorting-searching-aspnet-core-webapi/

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯6👍3
Словарные выражения (Dictionary Expressions) скоро появятся в C#?

Хотели бы вы, чтобы их добавили в язык? 💚

Судя по этому , похоже, что могут завезти spread-оператор, и тогда при сборке нового словаря можно будет подмешивать/разворачивать другие словари.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥27👍4🤔4🤨2
В этом PR предлагается использовать io_uring для dotnet System.Net.Sockets на Linux

PR124374 заменяет epoll на managed C#-движок на io_uring: batching, multishot accept/recv, zero-copy send, registered buffers, SQPOLL (zero-syscall) и много всего еще

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
😐32👍1
Два варианта 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