C# Portal | Программирование
15K subscribers
918 photos
107 videos
24 files
763 links
Присоединяйтесь к нашему каналу и погрузитесь в мир для C#-разработчика

Связь: @devmangx

РКН: https://clck.ru/3FocB6
Download Telegram
API Gateway и Load Balancer часто путают новички, но это база для любого бэкенд разработчика

API Gateway  работает как входная точка

• это единая точка, куда стучатся клиенты, он берет на себя всю первичную обработку запросов
• умный слой, который управляет авторизацией и аутентификацией (AuthN), rate limiting, логированием, мониторингом запросов и маршрутизацией
• идеально подходит для микросервисов, потому что централизует управление API во всей системе
• работает на уровне приложения L7

Load Balancer  работает как распределитель трафика

• его задача распределять входящие запросы между группой одинаковых серверов
• нужен чтобы повысить пропускную способность, уменьшить задержки и не допускать перегрузки одного сервера
• подходит для систем с высокой нагрузкой, где важна отказоустойчивость и стабильное распределение трафика
• может работать как на уровне приложения L7, так и на транспортном уровне L4

API Gateway управляет, валидирует и контролирует API запросы (про сложность)

Load Balancer равномерно распределяет трафик и оптимизирует нагрузку (про производительность)

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍92😁1
C# 14: Extension Members и как они помогают polyfill-библиотекам

C# давно поддерживает extension-методы, которые позволяют добавлять новые методы к существующим типам, не трогая исходный код. Но раньше это касалось только инстанс-методов. В C# 14 появились extension members, и область расширения заметно выросла. Теперь можно добавлять extension-свойства и extension-статические методы к существующим типам. Такая фича особенно полезна для polyfill-библиотек, которые позволяют использовать новые API в старых версиях .NET.

Polyfill-библиотеки помогают писать код с использованием современных API, но при этом таргетировать разные версии .NET. В предыдущем посте про polyfills я объяснял, что смысл таких библиотек заключается в том, чтобы предоставить реализацию новых API для старых фреймворков и уменьшить количество #if в проекте.

До выхода C# 14 polyfill-библиотеки могли добавлять только инстанс-методы через extension methods. Это означало, что статические методы и свойства из новых версий .NET нормально не покрывались polyfill'ами. C# 14 снимает это ограничение и позволяет polyfill-библиотекам выглядеть куда полнее и нативнее.

Хороший пример это статический метод ArgumentNullException.ThrowIfNull, который появился в .NET 6. Он дает удобный способ проверить аргументы на null:

public void ProcessData(string data)
{
ArgumentNullException.ThrowIfNull(data);

// Process the data...
}


До C# 14 polyfill-библиотеки не могли добавить ThrowIfNull как статический метод ArgumentNullException, потому что extension методы работали только с инстансами. В C# 14 можно определить extension static method, и polyfill-библиотеки могут предоставить идентичный API даже для старых версий .NET. Теперь можно использовать ArgumentNullException.ThrowIfNull независимо от target framework, и код будет выглядеть единообразно и чище.

static class PolyfillExtensions
{
extension(ArgumentNullException)
{
public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null)
{
if (argument is null)
throw new ArgumentNullException(paramName);
}
}
}


Конечно, можно писать свои extension static-методы и свойства, но проще воспользоваться уже поддерживаемой polyfill-библиотекой.

Пакет Meziantou.Polyfill (GitHub) может покрывать более 350 типов, методов и свойств. Он использует возможности C# 14, чтобы дать максимально полное polyfill-решение. Пакет основан на source generators, которые автоматически добавляют только те polyfills, которые реально нужны под ваш target framework.

Установка: dotnet add package Meziantou.Polyfill

После установки можно использовать новые API вроде ArgumentNullException.ThrowIfNull даже при таргете на старые платформы, вроде .NET Standard 2.0 или .NET Framework:

public class UserService
{
public void CreateUser(string username, string email)
{
// Работает в .NET Standard 2.0 благодаря extension static methods
ArgumentNullException.ThrowIfNull(username);
ArgumentNullException.ThrowIfNull(email);

// Create user logic...
}
}


Source generator автоматически определяет target framework, версию C# и опции компиляции, и генерирует extension members только тогда, когда это возможно и реально требуется. Если вы не используете C# 14 или ваш target framework уже содержит нужный API, polyfill просто не будет создан.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍53🔥3
На Хабре разобрали YARP — это быстрый reverse proxy на .NET, и его можно удобно конфигурировать прямо в ASP.NET Core через appsettings.json.

Под капотом есть всё, что нужно для продовой нагрузки:

• поддержка HTTP/2 и gRPC
• трансформации путей и заголовков
• балансировка трафика (RoundRobin, PowerOfTwoChoices и другие алгоритмы)
• health-checks и session affinity
• TLS-терминация

В статье есть разбор, примеры конфигурации и демо от OTUS

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍64🔥2
C# expression-bodied члены просто топ.

А теперь к делу. Вот способ использовать enum для сортировки строк, которые сами по себе ничего не значат.

Бывало, что нужно отсортировать данные, пришедшие откуда-то извне, например из веб-API, где нет нормального поля для сортировки? Enum может стать небольшим хаком и избавить от магических строк.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
8
В Visual Studio 2026 появилась возможность отключать отображение символов под файлами C# и C++ в Solution Explorer, и эта функция скоро станет доступна.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15😁6👎21👏1
Оптимизация производительности создания GUID в .NET приложениях

Во многих .NET проектах GUID используют как идентификаторы для активностей, interop-сценариев, ключей в базах данных и многого другого. Часто можно встретить код вида new Guid("01234567-8901-2345-6789-012345678901"). Такой способ читаемый и простой, но у него есть скрытая цена = он может замедлять старт приложения.

Важно отметить, что это микрооптимизация, которая в основном полезна в случаях, когда GUID создаются во время запуска приложения. Для обычного кода разница практически незаметна (смотри бенчмарк ниже).


Создание GUID из строки требует парсинга строкового представления, а это уже дополнительные операции. Плюс .NET поддерживает несколько форматов строковых GUID (с фигурными скобками, без, с дефисами и т.д.), из-за чего логика парсинга тоже усложняется.

Если GUID зашит в коде, эту нагрузку можно убрать, используя конструктор Guid, который принимает числовые параметры.

Например, строковое создание GUID:

var guid = new Guid("01234567-8901-2345-6789-012345678901");


Можно заменить на:

var guid = new Guid(0x01234567, 0x8901, 0x2345, 0x67, 0x89, 0x01, 0x23, 0x45, 0x67, 0x89, 0x01);


Так GUID собирается напрямую из числовых компонентов, без парсинга строки. Минус в том, что читаемость страдает, и вручную конвертировать строку в numeric-формат легко ошибиться. Плюс такой код сложнее искать в проекте. Решением будет оставлять строковое значение как комментарий.

Автоматическое обнаружение с Meziantou.Analyzer

Чтобы находить такие места автоматически, Meziantou.Analyzer содержит правило MA0176, которое ищет создание GUID из строковых литералов и предлагает использовать numeric-конструктор.

Устанавливается через .NET CLI или добавлением в csproj.

CLI: dotnet add package Meziantou.Analyzer

Или в .csproj:

<PackageReference Include="Meziantou.Analyzer" Version="2.0.224">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>


MA0176 ловит такие случаи и предлагает оптимизацию:

// Строковое создание (будет замечено анализатором)
_ = new Guid("01234567-8901-2345-6789-012345678901");
_ = Guid.Parse("01234567-8901-2345-6789-012345678901");


И автоматически заменяет на:

// Оптимизированный вариант
new Guid(0x01234567, 0x8901, 0x2345, 0x67, 0x89, 0x01, 0x23, 0x45, 0x67, 0x89, 0x01) /* 01234567-8901-2345-6789-012345678901 */;

Бенчмарк

Использовался BenchmarkDotNet для сравнения:

public class NewGuid
{
[Benchmark(Baseline = true)]
public Guid GuidParse() => Guid.Parse("01234567-8901-2345-6789-012345678901");

[Benchmark]
public Guid NewGuidString() => new Guid("01234567-8901-2345-6789-012345678901");

[Benchmark]
public Guid NewGuidComponents() => new Guid(0x01234567, 0x8901, 0x2345, 0x67, 0x89, 0x01, 0x23, 0x45, 0x67, 0x89, 0x01);
}


GuidParse:
Mean 23.4168 ns
Error 0.4823 ns
StdDev 0.4511 ns
Median 23.3622 ns

NewGuidString:
Mean 22.6531 ns
Error 0.3757 ns
StdDev 0.3514 ns
Median 22.7011 ns

NewGuidComponents:
Mean 0.0215 ns
Error 0.0170 ns
StdDev 0.0366 ns
Median 0.0000 ns


Разница огромная (×1000 в пользу числового конструктора), но в большинстве приложений создаётся мало фиксированных GUID, поэтому влияние на общую производительность минимальное. Но при старте приложения, а особенно в сценариях вроде Azure Functions или AWS Lambda, где важны cold start задержки, эта оптимизация может помочь.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
6🤔6👍3🥴2
Polly остаётся основным выбором для реализации отказоустойчивости в .NET, особенно теперь, когда он быстрее в V8+, и это важно, потому что HTTP-запросы могут падать из-за сетевых или серверных проблем, создавая риск каскадных отказов, а официальная библиотека resilience в .NET всего лишь обёртка над Polly с OpenTelemetry.

Вот как сделать приложение устойчивым: читать

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍3
Перегрузка операторов в C# 14 выходит на новый уровень и теперь вопрос только в том, стоит ли принимать такие замороченные варианты в кодовой базе, потому что подобные штуки могут взорвать мозг многим разработчикам

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯34👍127👏1
Не злоупотребляй select * в запросах к базе.

Почему это плохо:

Схема может измениться, появятся новые поля, например огромная JSONB колонка, и ты внезапно начнешь таскать лишний трафик и грузить память.

Нельзя нормально использовать covering indexes, потому что запрос требует все колонки, а не только нужные.

Запрашивай только те поля, которые реально нужны в проде.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🥴3
Разбираемся с основами работы с FakeLogger.

Мокать зависимости бывает неудобно, особенно если интерфейс не принадлежит вам, и взаимодействие с ним в коде идет через extension-методы. Так обстоит дело с ILogger из базовой инфраструктуры логирования Microsoft. Сам интерфейс ILogger довольно простой, но на практике вы почти никогда не работаете с ним напрямую. Тогда как протестировать, что вызывается правильный метод и параметры, чтобы убедиться что логирование работает корректно?

Раньше я писал о том, как замокать ILogger<> через Moq, передать его в SUT (system under test) и проверить вызовы. Это рабочий вариант, но он громоздкий, плюс код проверки приходится копировать между проектами. Не лучший вариант.

И тут появляется FakeLogger!

FakeLogger дает полный доступ ко всем данным, которые проходят через вызовы ILogger в вашем коде, и позволяет удобно писать ассерты в тестах.

Пакет немного переезжал, но сейчас он находится в Microsoft.Extensions.Diagnostics.Testing и доступен через пространство имен Microsoft.Extensions.Logging.Testing, как и ожидаешь.

После того как вы подключили NuGet-пакет в проект с тестами, можно начинать использовать его в юнит-тестах.

Простой пример

Для демонстрации возьмем небольшой SUT.

public class DemoService(ILogger<DemoService> logger)
{
public void LogMe()
{
logger.LogInformation("This should be logged.");
}
}


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

Начнем с создания экземпляра FakeLogger и SUT:

var fakeLogger = new FakeLogger<DemoService>();
var sut = new DemoService(fakeLogger);


FakeLogger<> реализует нужный интерфейс, так что мы можем просто передать его в DemoService.

После выполнения Act-части теста (в AAA-подходе) можно проверять результат:

fakeLogger.LatestRecord.Message.Should().Be("This should be logged.");


Самый простой способ — обратиться к свойству LatestRecord у fakeLogger. Оно содержит последний вызов логгера внутри SUT. На этом базовое использование заканчивается.

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

Вы все еще пишете свою обертку или уже переходите на FakeLogger? 🐵

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🔥3
Уже полгода в программировании, а в голове каша?

Знакомо? Дело не в вас.
Вас подвела система: куски информации без структуры и обратной связи.

Нашел годный курс Основы программирования:
✔️ Практика с первого дня, а не через месяц скучной теории
✔️ Поддержка от команды курса. Всегда отвечают на вопросы
✔️ Нет привязки ко времени. Можете учиться в своем темпе, когда угодно и сколько угодно

Прямо сейчас идет Чёрная Пятница — скидка 20%

98 630 человек уже начали учиться.
Посмотрите *1 031* реальных отзывов и начните наконец двигаться к цели.

👉 Даже можно попробовать проходить курс бесплатно:
https://stepik.org/lesson/1263653/step/1?unit=1277776

А если основы C# уже знаете, у нас есть продвинутые курсы:
https://stepik.org/org/PRO_csharp
4🔥4👌3👍1
Ты можешь ловить уязвимости в коде ещё на этапе сборки.

Вот простой способ сделать это в .NET буквально за пару минут.

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

Статический анализ кода позволяет это сделать.

Вот пример ошибки при билде, где меня предупреждают, что мой код для хеширования пароля небезопасен. 👇

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

Если хочешь посмотреть, как настроить это с нуля, загляни в статью

Имей в виду, что такой подход не поймает абсолютно все проблемы.

Если хочешь пойти глубже, посмотри в сторону более мощных инструментов вроде SonarQube.

@KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
4🔥1🤔1
Разрабатывать приложения под Windows и добавлять туда AI-функции вроде распознавания изображений, генерации текста и прочего бывает долго и муторно, если настраивать модели и писать всё с нуля.

Поэтому Microsoft выложила в открытый доступ инструмент AI Dev Gallery. Он сделан специально для Windows-разработчиков и помогает быстро подключать модели и интегрировать разные AI-возможности.

Там есть больше 25 интерактивных примеров, которые покрывают работу с изображениями, текстом, голосом и другими задачами. Модели можно сразу просматривать и скачивать с Hugging Face и GitHub.

Каждый пример включает полный исходник на C#, и его можно экспортировать в самостоятельный проект Visual Studio одним кликом, чтобы дальше спокойно дорабатывать.

Установить можно через Microsoft Store или клонировать репозиторий и собрать проект в Visual Studio 2022 или новеe

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11🤔1
This media is not supported in your browser
VIEW IN TELEGRAM
⚡️Чёрная пятница на Stepik: забери -30% на курс по Linux!

Внутри 20+ модулей: от установки Linux и работы с файлами до сетей, прав, дисков, процессов, автоматизации на Bash и многого другого. Всё сразу закрепляется на практике (200+ заданий с автопроверкой).

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

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

Есть бесплатные демо-уроки для ознакомления. В ближайшие 48ч курс доступен со скидкой 30% по промокоду «STBLACKFRIDAY30»: открыть курс на Stepik
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21🍌1
Не делайте вот так в EF-запросах

Как решить проблему N+1 ⬇️

Оптимизация производительности:

Запрос внутри цикла:

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

Запрос вне цикла:

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

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🥴4💯4
Когда работаешь с Entity Framework Core, важно понимать, как работает слежение за изменениями (change tracking). Это влияет и на производительность, и на корректность данных. Я знал про метод AsNoTracking(), но во время code review наткнулся на менее известную, но очень полезную альтернативу: AsNoTrackingWithIdentityResolution().

Разберёмся, чем этот метод отличается и когда его стоит применять.

Коротко: что делает AsNoTracking()?

Прежде чем перейти к AsNoTrackingWithIdentityResolution, напомним, что делает AsNoTracking(). По умолчанию EF Core отслеживает все полученные сущности в change tracker. Это позволяет:

- автоматически фиксировать изменения объектов
- выполнять обновления без явного Attach
- обеспечивать identity resolution (один объект в памяти на одну запись в базе)

Но отслеживание имеет накладные расходы. Если запрос read-only и обновлять данные не нужно, то AsNoTracking() повышает производительность, отключая tracking целиком.

// Без отслеживания — быстрее для read-only сценариев

var customers = await context.Customers
.AsNoTracking()
.ToListAsync();


Хотя AsNoTracking() ускоряет работу, он создаёт тонкий, но важный побочный эффект: дублирующиеся сущности. Так как change tracker не занимается identity resolution, в памяти могут появляться несколько экземпляров одной и той же сущности.

Пример:

var orders = await context.Orders
.AsNoTracking()
.Include(o => o.Customer)
.Where(o => o.OrderDate > DateTime.Now.AddDays(-30))
.ToListAsync();


Если несколько заказов принадлежат одному и тому же пользователю, EF создаст отдельные экземпляры Customer для каждого заказа. В итоге это приводит к:

- лишнему потреблению памяти
- проблемам при сравнении объектов
- потере целостности графа объектов

Появляется AsNoTrackingWithIdentityResolution()

AsNoTrackingWithIdentityResolution() решает эту проблему и даёт баланс между:

- отсутствием tracking (производительность)
- сохранением identity resolution (один объект на сущность)

var orders = await context.Orders
.AsNoTrackingWithIdentityResolution()
.Include(o => o.Customer)
.Where(o => o.OrderDate > DateTime.Now.AddDays(-30))
.ToListAsync();

// Теперь несколько заказов с одним Customer будут ссылаться на один экземпляр


Как работает AsNoTrackingWithIdentityResolution():

- EF не добавляет сущности в change tracker
- во время материализации создаётся временная identity map
- сущности с одинаковым ключом указывают на один объект
- identity map удаляется после выполнения запроса

То есть мы получаем identity resolution при выполнении запроса, но без постоянных затрат tracking.

Если этот режим нужен часто, можно выставить его по умолчанию:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(connectionString)
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTrackingWithIdentityResolution);
}


Когда что использовать

✔️ Default Tracking если:

- нужно обновлять, удалять или отслеживать изменения
- небольшой набор данных
- требуется автоматическое обнаружение изменений

✔️ AsNoTracking если:

- запрос read-only
- нет навигационных свойств или связанных сущностей
- критична максимальная производительность
- результат не содержит повторяющихся сущностей

✔️ AsNoTrackingWithIdentityResolution если:

- read-only запрос с Include/Join
- важна целостность графа объектов
- результаты содержат повторяющиеся сущности
- нужно сравнение объектов по ссылке

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍174
Please open Telegram to view this post
VIEW IN TELEGRAM
😁13🤯4👏3🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
🔥 Чёрная пятница на Stepik: забери -25% на курс: "Грокаем паттерны SQL-собеседований"

Преподаватели берут то, с чем сами сталкиваются в повседневной работе Data Scientist, AI/ML Engineer, Data Engineer и Data Analyst, и превращают это в понятные практические упражнения.

Задачи в духе LeetCode, разбор живых кейсов, приёмы оптимизации. Всё, что позволяет уверенно чувствовать себя на собеседовании и дальше уже в команде.

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

В ближайшие 48ч курс доступен со скидкой 25% по промокоду «BLACKFRIDAY25»: открыть курс на Stepik
Please open Telegram to view this post
VIEW IN TELEGRAM
На Reddit обсуждают, каких возможностей не хватает C# и .NET. Автор поста поделился своим списком пожеланий, сформированным за годы работы с языком.

Среди предложений:

• статическое наследование как способ переиспользования логики без необходимости создавать экземпляры классов
• ключевое слово для гарантированного использования структур без распределения в куче
• принудительное инлайнинг методов, а не лишь намек компилятору
• ручной контроль над GC, похожий на подход Unity, чтобы критичный код не зависел от решений рантайма
• возможность создавать классы, которые полностью живут в unmanaged-памяти
• компактные ключевые слова для базовых типов, которые в .NET выглядят излишне громоздко


Автор подчёркивает, что C# ему нравится, но язык мог бы развиваться в сторону большей прозрачности и контроля над производительностью.

Какие функции вы хотели бы видеть в C# и dotnet? 🤔

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🥴4🤨1