Когда вы пишете асинхронный метод вроде:
async Task<int> GetDataAsync()
{
var data = await FetchAsync();
return data.Length;
}
вы можете думать, что это «всего лишь ожидание задачи». Но на самом деле компилятор C# превращает этот метод в state machine — конечный автомат, который управляет переходами между состояниями выполнения.
State machine — это структура, которая:
• хранит текущее состояние выполнения программы, например, до await, после await или завершено,
• знает, в какое состояние перейти дальше, когда наступает внешнее событие например, завершение Task.
Проще говоря, компилятор разворачивает ваш линейный асинхронный код в набор состояний + переключатель между ними.
Что делает компилятор
При компиляции метода с await создаётся вспомогательный класс, реализующий интерфейс
IAsyncStateMachine.Если упростить, то наш пример превращается примерно в:
private struct GetDataAsyncStateMachine : IAsyncStateMachine
{
public int _state;
public AsyncTaskMethodBuilder<int> _builder;
private TaskAwaiter<string> _awaiter;
public void MoveNext()
{
try
{
if (_state == 0)
{
// после await
var result = _awaiter.GetResult();
_builder.SetResult(result.Length);
return;
}
var task = FetchAsync();
if (!task.IsCompleted)
{
_state = 0;
_awaiter = task.GetAwaiter();
_builder.AwaitUnsafeOnCompleted(ref _awaiter, ref this);
return;
}
_builder.SetResult(task.Result.Length);
}
catch (Exception ex)
{
_builder.SetException(ex);
}
}
public void SetStateMachine(IAsyncStateMachine stateMachine) { }
}
Другие примеры использования state machines в C#
• yield return — генерация итераторов
IEnumerator• foreach на async коллекциях — асинхронные итераторы
IAsyncEnumerableКаждый await или yield превращает метод в "автомат", который сам управляет своим ходом выполнения.
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🥱7❤5
🚀 JetBrains .NET Days 2025
JetBrains снова собрала лучших инженеров и евангелистов .NET на своей онлайн-конференции .NET Days. Два дня плотных докладов — от чистой архитектуры и распределённых систем до GenAI и F#.
1️⃣ Самое интересное за первый день:
• Чистая архитектура с ASP.NET Core
Как выстроить проект так, чтобы код был читаемым, тестируемым и легко рефакторился. Разделение слоёв и зависимостей без боли.
• Nullability в C#: включаем защиту от NullReferenceException
Аннотации и статический анализ помогают избавиться от NullReferenceException и внедрить null safety даже в старый код.
• Как выбрать систему обмена сообщениями
Сравнение AWS SQS, RabbitMQ и Azure Service Bus — плюсы, минусы и типичные ошибки при выборе очередей сообщений.
• TDD на фронтенде с Blazor
Как применять TDD на фронтенде с Blazor и bUnit. Быстрая обратная связь и уверенность в каждом изменении.
2️⃣ Темы за второй день:
• Версионирование событийных систем
Безопасные приёмы эволюции событий: версионирование, апкастинг и совместимость без поломок у потребителей.
• Генеративный ИИ и .NET Aspire в действии
Интеграция LLM, управление контекстом и масштабируемая оркестрация с помощью Semantic Kernel и Aspire.
• Функциональное программирование в F#: мода или польза
Реальные преимущества функционального подхода — лаконичные пайплайны, паттерн-матчинг и безопасные абстракции.
• Как нашли утечку 2 ГБ в день за 5 минут
История о том, как обнаружить гигабайтные утечки памяти с помощью dotMemory в продакшене за считанные минуты.
Два дня — и десятки инсайтов о будущем .NET: от облаков и AI до функционального подхода и устойчивой архитектуры. Записи доступны на YouTube.
➡️ Первый день
➡️ Второй день
🐸 Библиотека шарписта
#il_люминатор
JetBrains снова собрала лучших инженеров и евангелистов .NET на своей онлайн-конференции .NET Days. Два дня плотных докладов — от чистой архитектуры и распределённых систем до GenAI и F#.
• Чистая архитектура с ASP.NET Core
Как выстроить проект так, чтобы код был читаемым, тестируемым и легко рефакторился. Разделение слоёв и зависимостей без боли.
• Nullability в C#: включаем защиту от NullReferenceException
Аннотации и статический анализ помогают избавиться от NullReferenceException и внедрить null safety даже в старый код.
• Как выбрать систему обмена сообщениями
Сравнение AWS SQS, RabbitMQ и Azure Service Bus — плюсы, минусы и типичные ошибки при выборе очередей сообщений.
• TDD на фронтенде с Blazor
Как применять TDD на фронтенде с Blazor и bUnit. Быстрая обратная связь и уверенность в каждом изменении.
• Версионирование событийных систем
Безопасные приёмы эволюции событий: версионирование, апкастинг и совместимость без поломок у потребителей.
• Генеративный ИИ и .NET Aspire в действии
Интеграция LLM, управление контекстом и масштабируемая оркестрация с помощью Semantic Kernel и Aspire.
• Функциональное программирование в F#: мода или польза
Реальные преимущества функционального подхода — лаконичные пайплайны, паттерн-матчинг и безопасные абстракции.
• Как нашли утечку 2 ГБ в день за 5 минут
История о том, как обнаружить гигабайтные утечки памяти с помощью dotMemory в продакшене за считанные минуты.
Два дня — и десятки инсайтов о будущем .NET: от облаков и AI до функционального подхода и устойчивой архитектуры. Записи доступны на YouTube.
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7🔥4👍3
В C# каждое обращение к элементу массива проверяется на выход за границы. Это безопасно, но медленно. Разработчики часто решают проблему через unsafe-код с указателями — быстро, но опасно: один неверный индекс, и приложение крашится или получает дыру в безопасности.
Есть третий путь —
Span<T>Span — это структура, которая хранит указатель на данные и их длину. Фишка в том, что это ref struct — она живёт только в стеке и не может попасть в кучу. Благодаря этому компилятор гарантирует: данные переживут span, а значит проверки границ можно убрать.
Например, в быстрой сортровке вместо передачи массива с индексами low/high передаёте span. Код короче, переполнение невозможно, а рекурсивные вызовы работают через срезы:
// Было: опасно, (low + high) может переполниться
void Quicksort(int[] array, int low, int high) {
int mid = (low + high) / 2; // 💥 overflow!
// ...
Quicksort(array, low, pivot - 1);
Quicksort(array, pivot + 1, high);
}
// Стало: безопасно и выразительно
void Quicksort(Span<int> span) {
if (span.Length <= 1) return;
int pivot = Partition(span);
Quicksort(span[..pivot]); // левая часть
Quicksort(span[(pivot + 1)..]); // правая часть
}
Раньше для передачи части массива в функцию приходилось либо копировать данные, либо таскать массив + offset + length. Span решает это элегантно: создаёте срез array[10..20], передаёте дальше — никакого копирования, полная безопасность типов.
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17⚡1❤1😁1
List<T> — это универсальный солдат C#. Но универсальный не значит оптимальный. Пока вы перебираете 10 000 элементов через .FirstOrDefault(), Dictionary находит нужное за одно обращение. И это только начало.Queue — когда порядок имеет значение
Обрабатываете задачи по очереди? Вот вам готовое решение:
var tasks = new Queue<UserRequest>();
tasks.Enqueue(new UserRequest { Id = 1, Name = "Alice" });
tasks.Enqueue(new UserRequest { Id = 2, Name = "Bob" });
// Безопасное извлечение (с .NET 6+)
while (tasks.TryDequeue(out var request))
{
await ProcessAsync(request);
}
Первым пришел — первым обработался. Никаких индексов, никаких сортировок.
Stack — для истории действий
Делаете undo/redo или навигацию? Стек решает это из коробки:
var history = new Stack<string>();
history.Push("/home");
history.Push("/products");
history.Push("/cart");
// Вернуться назад
if (history.TryPop(out var lastPage))
{
Console.WriteLine($"Back to: {lastPage}"); // /cart
}
Последнее действие всегда доступно первым. Логика браузерной кнопки «Hазад» готова.
Dictionary — когда нужна скорость
Поиск по ключу за O(1) вместо перебора всего списка:
var users = new Dictionary<int, User>();
users.Add(1, new User { Name = "Alice" });
users.Add(2, new User { Name = "Bob" });
// Безопасная проверка
if (users.TryGetValue(2, out var user))
{
Console.WriteLine(user.Name); // Bob
}
Никаких .FirstOrDefault(x => x.Id == 2) с перебором тысяч записей.
List отлично работает, когда вам нужен доступ по индексу и порядок элементов. Но если задача укладывается в одну из этих трёх структур — используйте их.
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱24❤10
Представьте, что вы отправили друга в магазин с деньгами на общий счёт. Он может потратить их на что угодно, при этом никому не отчитываясь. Вот примерно так же работает
static с изменяемым состоянием. Удобно? Да. Безопасно? Совсем нет.Почему
static плохо сочетается с даннымиStatic — это ключевое слово, которое любят новички и используют для удобства. А потом из этого вырастают проблемы, которые отъедают часы отладки. Главная беда в том, что статические поля существуют всё время работы программы и доступны отовсюду.
Четыре золотых правила
1. Статические поля могут быть только для чтения. Лучше — константы.
2. Статические методы должны работать как математические функции: дали параметры — получили результат. Никаких побочных эффектов, никаких изменений состояния. Например:
Select, Where, First из LINQ — это всё статические методы расширения, которые не трогают исходную последовательность.3. Если в классе есть методы, которые не используют поля экземпляра, эту логику лучше вынести в отдельный статический класс-помощник. Это делает код чище, тесты проще.
4. Если вы создаёте статическое поле, пусть оно указывает на что-то, что не меняется. На объект конфигурации? Хорошо. На список данных, который вы потом модифицируете? Катастрофа.
Когда
static — ваш друг• Вспомогательные классы с чистыми функциями
Создайте статический класс
StringUtils с методом static string ToPascalCase(string input). Нет состояния, нет побочных эффектов, нет проблем. Это хорошее использование.• Методы расширения для преобразований
Select, Where и компания — статические методы расширения, которые берут данные, преобразуют их и возвращают новые. Функциональный подход, никакого волшебства.• ThreadStatic для действительно отдельного состояния
Если каждому потоку нужно своё состояние, можно использовать
ThreadStatic. Но даже здесь нужна осторожность — каждый поток всё равно должен управлять своим состоянием.Передайте зависимость через конструктор, используйте DI-контейнер, создайте объект на сессию. Ваш будущий я, сидящий в отладчике в три утра, скажет вам спасибо.
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
В C# 14 расширяется концепция расширений: можно создавать не только расширенные методы для экземпляров типов, но и свойства расширения, а также расширения, которые относятся к самому типу как к статическому элементу.
Пример:
public static class EnumerableExtensions
{
extension<TSource>(IEnumerable<TSource> source)
{
public bool IsEmpty => !source.Any(); // Расширенное свойство
}
extension<TSource>(IEnumerable<TSource>)
{
public static IEnumerable<TSource> Combine(IEnumerable<TSource> first, IEnumerable<TSource> second)
=> first.Concat(second); // Статический метод расширения
}
}
Конструкция
extension<TSource>(IEnumerable<TSource> source) — это новый синтаксис блока расширения для экземпляра типа: здесь определяются расширяющие члены, которые «прикрепляются» к объекту IEnumerable<TSource>.Внутри блока объявлено свойство
IsEmpty, которое возвращает true, если последовательность пуста (!source.Any()).Второй блок
extension<TSource>(IEnumerable<TSource>) — расширение для самого типа IEnumerable<TSource> как статического члена.Внутри него определён статический метод Combine, который принимает две последовательности и объединяет их с помощью Concat.
Как это использовать:
var list = new List<int> { 1, 2, 3 };
bool empty = list.IsEmpty; // false — вызов расширенного свойства как у экземпляра
var combined = Enumerable<int>.Combine(new[] { 1 }, new[] { 2, 3 }); // {1, 2, 3}#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥5❤3😁3
Управление данными в свойствах теперь проще с новым ключевым словом для доступа к автоматически созданному полю.
Раньше для добавления логики установки значения использовалось отдельное поле и аксессоры:
private string _msg;
public string Message
{
get => _msg;
set => _msg = value ?? throw new ArgumentNullException(nameof(value));
}
Сейчас field ссылается на поле, которое создает компилятор, сокращая код:
public string Message
{
get;
set => field = value ?? throw new ArgumentNullException(nameof(value));
}
Минимум кода — максимум контроля: write-only свойства снаружи и строгая логика внутри.
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21❤2😁2
🛠 Натуральные преобразования для Span и ReadOnlySpan
C# 14 позволяет неявно преобразовывать между
Типы
🐸 Библиотека шарписта
#il_люминатор
C# 14 позволяет неявно преобразовывать между
Span<T>, ReadOnlySpan<T> и массивами, упрощая манипуляции с данными и повышая скорость:ReadOnlySpan<char> roSpan = "hello";
Span<char> span = roSpan;
char[] array = roSpan.ToArray();
Типы
Span теперь могут выступать как принимающие методы расширения, то есть станут получателями вызова таких методов. Они также могут комбинироваться с другими типами преобразований.#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤2⚡1
Вы пишете библиотеку на C#, используете async/await, и всё работает. Но потом ваша библиотека попадает в веб-приложение, мобильное приложение, или какой-то другой контекст — и начинаются странные deadlock'и или даже зависания.
Проблема вот в чём: когда код выходит из await, он хочет продолжить выполнение в том же контексте, в котором был запущен. Если это UI-приложение, контекст одноточечный — только главный поток может работать с UI.
Если ваша библиотека захватит этот контекст и не отпустит — приложение зависнет. А если написали синхронный код, который вызывает async библиотеку, и она захватит контекст — будет deadlock.
ConfigureAwait(false) как раз говорит: «Мне вообще всё равно, в каком контексте продолжать. Возьми любой поток из thread pool'а«. И вот это спасает приложения от проблем:await stream.WriteAsync(buffer, ct).ConfigureAwait(false);
Результат: библиотека не блокирует контекст, не создаёт deadlock'и, работает везде одинаково.
Когда НЕ нужен ConfigureAwait(false):
В приложениях можно писать без него, если вам нужен контекст:
// В ASP.NET контроллере — можно без ConfigureAwait(false)
[HttpGet]
public async Task<IActionResult> GetData()
{
var data = await service.GetDataAsync(); // Контекст нужен для логирования, etc
return Ok(data);
}
// В UI приложении — нужен контекст
private async void Button_Click(object sender, EventArgs e)
{
var data = await LoadDataAsync(); // Нужно вернуться в UI поток
UpdateUI(data);
}
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍11🥱1
🧑💻 Контролируем параллельную работу
Представьте ситуацию: у вас есть 1000 элементов для обработки, и вы хотите запустить это параллельно. Но если просто запустить все одновременно, приложение упадёт — будет слишком много одновременных подключений к БД, API или файловой системе.
Здесь помогает
Как это работает
Создаём семафор с лимитом 4. Это значит, что максимум 4 потока могут находиться внутри защищённого блока одновременно.
Поток ждёт в очереди, пока не появится "место". Если уже 4 потока внутри — новичок подождёт:
После обработки обязательно вызываем
Вместо 100 одновременных загрузок будет ровно столько, сколько вы укажете.
🔹 Алгоритмы и структуры данных
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib
🐸 Библиотека шарписта
#il_люминатор
Представьте ситуацию: у вас есть 1000 элементов для обработки, и вы хотите запустить это параллельно. Но если просто запустить все одновременно, приложение упадёт — будет слишком много одновременных подключений к БД, API или файловой системе.
Здесь помогает
SemaphoreSlim. Как это работает
Создаём семафор с лимитом 4. Это значит, что максимум 4 потока могут находиться внутри защищённого блока одновременно.
await gate.WaitAsync(ct);
Поток ждёт в очереди, пока не появится "место". Если уже 4 потока внутри — новичок подождёт:
await gate.WaitAsync(ct);
После обработки обязательно вызываем
Release() — это освобождает место для следующего потока. finally гарантирует, что это произойдёт даже если операция упадёт:try { await Process(x, ct); }
finally { gate.Release(); }Вместо 100 одновременных загрузок будет ровно столько, сколько вы укажете.
🔹 Алгоритмы и структуры данных
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11
Новый уровень модульности в больших проектах: делим объявление и реализацию. Большие проекты требуют таких гибких способов организации кода.
В C# теперь можно объявлять события и конструкторы с модификатором partial. Это значит, что объявление может находиться в одном месте, а его реализация — в другом, обычно в другом файле:
public partial class MyClass
{
partial void OnInitialized();
public MyClass()
{
OnInitialized();
}
}
Здесь объявлена частичная функция
OnInitialized() без тела — её реализация может появиться отдельно. Также конструктор вызывает эту функцию, если она реализована.Частичные конструкторы поддерживают совместимость с платформами, использующими генерацию кода.
🔹 Практический интенсив «Архитектуры и шаблоны проектирования»
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔5❤2🥱1
Представьте фоновую задачу, которая должна срабатывать каждые 5 секунд: проверка очереди, синхронизация данных, отправка метрик. Можно, конечно, использовать
Task.Delay, но это хрупко — если одна итерация длится дольше, чем ожидалось, ритм собьётся.PeriodicTimer решает эту проблему иначе:var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));
Создаём таймер, который будет срабатывать каждые 5 секунд. Точка отсчёта — момент создания.
while (await timer.WaitForNextTickAsync(ct))
{
await DoWork(ct);
}
WaitForNextTickAsync ждёт следующего тика таймера. Когда он приходит — выполняем работу. Если работа заняла 1 секунду, следующий тик всё равно придёт через 5 секунд от начала цикла, а не через 6.Это стабильный интервал, а не интервал между завершением одной работы и началом другой.
🔹 Практический интенсив «Архитектуры и шаблоны проектирования»
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20👍8
🛠 Как создать путь к временному файлу в C#
В C# для работы с путями к файлам существует класс
Другой полезный метод —
Пример:
Использование
🔸 Основы IT для непрограммистов
🔸 Получить консультацию менеджера
🔸 Сайт Академии 🔸 Сайт Proglib
🐸 Библиотека шарписта
#il_люминатор
В C# для работы с путями к файлам существует класс
Path, который предлагает методы для объединения частей путей и получения системных директорий. Один из таких методов — GetTempPath(). Он возвращает путь к папке, предназначенной для временных файлов на текущей системе.Другой полезный метод —
Combine(). Он соединяет несколько частей пути, учитывая особенности слэшей в Windows, Linux и macOS. Вместо ручного склеивания строк и риска ошибок, Combine() гарантирует правильный формат.Пример:
using System;
using System.IO;
class Program
{
static void Main()
{
// Получаем путь к системной временной папке и добавляем имя файла
var path = Path.Combine(Path.GetTempPath(), "report.csv");
Console.WriteLine("Путь к временному файлу: " + path);
// Здесь можно сохранить файл, открыть или выполнить другие операции
}
}
Использование
Path.GetTempPath() и Path.Combine() — простой способ получить корректный путь к временному файлу без ошибок и с учётом особенностей операционных систем. 🔸 Основы IT для непрограммистов
🔸 Получить консультацию менеджера
🔸 Сайт Академии 🔸 Сайт Proglib
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍23🥱7
В обыденной работе с объектами часто приходится проверять, не равен ли объект null, прежде чем присваивать значение его свойствам. Это приводит к громоздким конструкциям с множеством if. Теперь с null-условным присваиванием можно написать проще и понятнее.
Операторы условного доступа
?. и ?[] теперь поддерживают использование слева от оператора присваивания. Раньше для безопасного присваивания требовалось писать так:if (customer is not null)
{
customer.Order = GetCurrentOrder();
}
С null-условным присваиванием можно упростить до одной строки:
customer?.Order = GetCurrentOrder();
Особенность в том, что правая часть вычисляется только если левая часть не равна null. Если customer равен null, метод
GetCurrentOrder() вызван не будет. Это оптимизирует производительность и упрощает логику.Также это работает с составными операторами присваивания: +=, -=, *=, но не поддерживается для инкремента и декремента.
customer?.Total += 100;
// Но нельзя так:
// customer?.Total++;
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🥱5
🧑💻 Проекция колонок вместо сущностей в запросах
Когда работаете с базой данных, лучше получить только нужные поля, а не всю сущность. Это ускорит запросы и снизит объём передаваемых данных.
Пример проекции колонок с использованием LINQ:
Здесь из таблицы пользователей выбираются только Id и Name, которые помещаются в объект UserDto.
🔹 ML для старта в Data Science
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib
🐸 Библиотека шарписта
#il_люминатор
Когда работаете с базой данных, лучше получить только нужные поля, а не всю сущность. Это ускорит запросы и снизит объём передаваемых данных.
Пример проекции колонок с использованием LINQ:
var dto = await db.Users
.Where(u => u.Id == id)
.Select(u => new UserDto(u.Id, u.Name))
.SingleOrDefaultAsync(ct);
Здесь из таблицы пользователей выбираются только Id и Name, которые помещаются в объект UserDto.
🔹 ML для старта в Data Science
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🥰2
🔑 Управление HttpClient по ключам
С .NET 9 можно управлять зависимостями по ключам. Буквально регистрировать и получать, к примеру, конфиги для HttpClient.
Как это работает
•
• Внедрение осуществляется с помощью атрибута
• В отличие от старых подходов с именованными или типизированными клиентами, внедрение зависимостей по ключам позволяет упростить код и уменьшить лишние вызовы фабрики
Пример регистрации и использования:
Преимущества Keyed DI
• Минимизирует необходимость вручную создавать и хранить экземпляры через
• Позволяет внедрять в сервисы и контроллеры правильно настроенные клиенты с простым указанием ключа.
• Улучшает читаемость и поддержку кода, особенно если в проекте много разных конфигураций HttpClient.
• Можно глобально включить ключевой DI для всех клиентов через
Важные моменты
• По умолчанию
• Нужно внимательно следить за временем жизни сервисов, чтобы избежать утечек памяти и проблем с соединениями.
• При ошибочном использовании ключа сервис выдаст стандартное исключение, помогая быстро находить и исправлять конфигурационные ошибки.
🔹 ML для старта в Data Science
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib
🐸 Библиотека шарписта
#il_люминатор
С .NET 9 можно управлять зависимостями по ключам. Буквально регистрировать и получать, к примеру, конфиги для HttpClient.
Как это работает
•
AddAsKeyed() — новый метод расширения для регистрации именованных HttpClient как Keyed Service в DI. Имя клиента становится ключом, по которому можно получать конкретный экземпляр.• Внедрение осуществляется с помощью атрибута
[FromKeyedServices("key")] или через Func<string, HttpClient> для динамического выбора клиента по ключу.• В отличие от старых подходов с именованными или типизированными клиентами, внедрение зависимостей по ключам позволяет упростить код и уменьшить лишние вызовы фабрики
IHttpClientFactory.Пример регистрации и использования:
var builder = WebApplication.CreateBuilder(args);
// Регистрация HttpClient с ключом "github"
builder.Services.AddHttpClient("github", c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "dotnet");
})
.AddAsKeyed();
var app = builder.Build();
// Внедрение HttpClient по ключу "github"
app.MapGet("/", ([FromKeyedServices("github")] HttpClient httpClient) =>
{
return httpClient.GetFromJsonAsync<Repo>("/repos/dotnet/runtime");
});
app.Run();
record Repo(string Name, string Url);
Преимущества Keyed DI
• Минимизирует необходимость вручную создавать и хранить экземпляры через
IHttpClientFactory.• Позволяет внедрять в сервисы и контроллеры правильно настроенные клиенты с простым указанием ключа.
• Улучшает читаемость и поддержку кода, особенно если в проекте много разных конфигураций HttpClient.
• Можно глобально включить ключевой DI для всех клиентов через
ConfigureHttpClientDefaults.Важные моменты
• По умолчанию
AddAsKeyed() регистрирует HttpClient с областью видимости Scoped, но можно настроить и другой жизненный цикл.• Нужно внимательно следить за временем жизни сервисов, чтобы избежать утечек памяти и проблем с соединениями.
• При ошибочном использовании ключа сервис выдаст стандартное исключение, помогая быстро находить и исправлять конфигурационные ошибки.
🔹 ML для старта в Data Science
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9🔥9👍3