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

Связь: @devmangx

РКН: https://clck.ru/3FocB6
Download Telegram
Планирование в Visual Studio теперь доступно в публичной превью-версии

Это обновление добавляет новый способ, с помощью которого Copilot может разруливать сложные, многошаговые задачи. Благодаря Planning Copilot теперь может изучать твой код, разбивать большие задачи на подзадачи и выполнять их пошагово, в итоге ты получаешь более предсказуемые результаты и лучше понимаешь, что именно он делает.

Мы уже видим обнадёживающие результаты: улучшилась успешность и стабильность работы разных моделей. Но это только начало


Попробуй в Visual Studio 2022 версии 17.14:
зайди в Tools → Options → Copilot → Enable Planning и включи функцию.

После этого расскажи, что думаешь.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
1🥴1
6 Шагов для Правильной Настройки Нового .NET-проекта

1. Задай единый стиль кода

Первое, что я добавляю в проект — файл .editorconfig.

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

Создать его можно прямо в Visual Studio.

Стандартная конфигурация - хороший старт, но можно подстроить под вкусы команды.

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

Вот два примера .editorconfig, которые можно использовать:

- из репозитория .NET runtime
- вариант для обычных .NET-проектов

2. Централизуй настройки сборки

Дальше я добавляю файл Directory.Build.props в корень решения. Он позволяет задавать настройки сборки, общие для всех проектов.

Пример:

<Project>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>


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

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

Фишка в том, что .csproj файлы становятся почти пустыми, кроме ссылок на NuGet-пакеты.

3. Централизуй управление пакетами

Когда решение разрастается, версии NuGet-пакетов в разных проектах начинают расходиться и чёрт, это превращается в кошмар 😬

Тут помогает централизованное управление пакетами.

Создай в корне файл Directory.Packages.props:

<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>

<ItemGroup>
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
<PackageVersion Include="SonarAnalyzer.CSharp" Version="10.15.0.120848" />
</ItemGroup>
</Project>


Теперь, когда подключаешь пакет в проекте, указываешь только имя, без версии:

<PackageReference Include="Microsoft.AspNetCore.OpenApi" />


Все версии хранятся централизованно. Обновлять зависимости становится проще, а рассинхрон версий между проектами исчезает.

Если нужно, можно переопределить версию в конкретном проекте.

4. Подключи статический анализ кода

Статический анализ помогает ловить потенциальные баги и следить за качеством кода. В .NET уже есть встроенные анализаторы, но я обычно добавляю SonarAnalyzer.CSharp, ведь он делает проверку глубже.

Установи пакет: Install-Package SonarAnalyzer.CSharp

И добавь его как глобальную зависимость в Directory.Build.props:

<ItemGroup>
<PackageReference Include="SonarAnalyzer.CSharp" />
</ItemGroup>


Дополнительно настрой сборку:

<Project>
<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisLevel>latest</AnalysisLevel>
<AnalysisMode>All</AnalysisMode>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
</Project>


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

Если какие-то правила не подходят, можно понизить их уровень до none в .editorconfig.

5. Настрой локальную оркестрацию

Чтобы у всех разработчиков среда была одинаковой, стоит настроить контейнерную оркестрацию.

Один из вариантов это .NET Aspire

.NET Aspire добавляет сервис-дискавери, телеметрию и упрощает конфигурацию. Все это встроено прямо в .NET-проекты.

Пример добавления проекта и Postgres-ресурса:

var postgres = builder.AddPostgres("demo-db");

builder.AddProject<WebApi>("webapi")
.WithReference(postgres)
.WaitFor(postgres);

builder.Build().Run();


Aspire под капотом использует Docker, но разработчику с ним работать приятнее.

В любом случае цель одна.

6. Автоматизируй сборку через CI

В конце я настраиваю простой workflow в GitHub Actions, чтобы проверять каждый коммит.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1710❤‍🔥1
Отличный плейлист для тех, кто только начинает изучать бэкенд

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
9
Многие приложения сильно завязаны на реляционные базы данных. Твое приложение, скорее всего, использует сложные запросы, ограничения на данные и прочие приятные фишки реляционных баз. А значит, большая часть поведения твоего приложения напрямую зависит от того, как именно ведет себя база данных.

Поэтому я стараюсь тестировать код на настоящей базе, а не на какой-нибудь подделке. Раньше это было проблемой — большинство СУБД сложно поднимать и автоматизировать. Сейчас всё проще. Используй TestContainers 🙄

TestContainers — это библиотека, которая поднимает Docker-контейнеры для тестов. Более того, в ней уже есть готовые конфигурации для популярных баз данных.

Сначала ставим библиотеку через NuGet. Потом настраиваем первый контейнер. Например, для Microsoft SQL Server:

// Конфигурируем базу, которую хотим поднять
var dbConfig = new MsSqlTestcontainerConfiguration
{
Password = "Test1234",
Database = "TestDB"
};

// Создаем контейнер с этой конфигурацией
var testContainer = new TestcontainersBuilder<MsSqlTestcontainer>()
.WithDatabase(dbConfig)
// Если не указать образ, MsSqlTestcontainerConfiguration выберет дефолтный
.WithImage("mcr.microsoft.com/mssql/server:2022-latest")
.Build();

// Запускаем контейнер
testContainer.StartAsync().Wait();


Этот код скачает Docker-образ Microsoft SQL Server и запустит контейнер. Потом ты можешь запросить строку подключения и гонять тесты против реальной базы. В примере ниже создается таблица Dogs, вставляется запись и читается обратно. Тест, как ни странно, упадет. Почему? Подсказка в конце поста

class Dog
{
public DateTime BirthDate { get; set; }
public string Name { get; set; }
}

using (var db = new SqlConnection(testContainer.ConnectionString))
{
// Для примера я использую Dapper
db.Execute(@"CREATE TABLE Dogs(
BirthDate DATETIME NOT NULL,
Name VARCHAR(MAX) NOT NULL
)");

var bornAt = new DateTime(2022, 12, 22, 12, 59, 59, 999);
db.Execute(@"INSERT Dogs(BirthDate,Name) VALUES(@BirthDate, @Name)", new
{
BirthDate = bornAt,
Name = "Joe"
});

var dog = db.Query<Dog>(@"SELECT * FROM Dogs").First();
// Этот assert упадет. Почему? Именно поэтому нужно тестировать на реальной базе.
Assert.AreEqual(bornAt, dog.BirthDate);
}


Очистка контейнера:

// Чистим за собой
testContainer.DisposeAsync();


Контейнеры автоматически очищаются

Интересный момент: если ты убьешь процесс, который гоняет тесты, что станет с контейнерами?

Запусти тест, потом посмотри, какие контейнеры сейчас активны: docker ps

Результат будет примерно такой:

roman@gamlor /tmp> docker ps
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
86a1da367f12 mcr.microsoft.com/mssql/server:2017... "/opt/mssql/bi..." Up 6s 49180->1433/tcp busy_bohr
5cec99b15206 testcontainers/ryuk:0.3.4 "/app" Up 7s 49179->8080/tcp
testcontainers-ryuk...


Как видно, поднялось два контейнера. Потом можно убить тестовый процесс и наблюдать за docker ps — контейнеры вскоре исчезнут.
Это и есть сила TestContainers. Контейнер testcontainers/ryuk следит за всеми тестовыми контейнерами и убивает их (вместе с собой), если теряет связь с тестовым процессом.

У TestContainers уже есть готовая поддержка множества баз данных. Можно поискать наследников класса TestcontainerDatabase. Аналогично для систем обмена сообщениями — они наследуются от TestcontainerMessageBroker. Полный список есть в документации.

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

var someContainer = new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("some-image")
.WithEnvironment("USER", "test-user")
.WithCommand("-flag-one -flag-two")
.Build();


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

P.S. Почему упал Assert? Потому что тип datetime в SQL Server имеет странные правила округления. Он может незаметно подправить значение даты. Так что снова повторим, тестируй на настоящей базе!

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
11👏1
Каждое приложение, которое ты используешь, общается с сервером через API.

Много лет стандартом был REST:

- роуты, которые представляют ресурсы (/users, /posts)
- классические методы: GET, POST, PUT, DELETE

Работает отлично, но когда приложение разрастается, начинаются проблемы:

Overfetching — сервер отдает больше данных, чем нужно. (Ты просишь имя, а получаешь весь профиль).

Underfetching — чтобы отобразить один экран, приходится делать несколько запросов. (Ты запрашиваешь посты, а комментарии приходят отдельно).

Жесткость — если меняется UI, приходится менять и бэкенд.

Главная проблема в подходе:

фронтенд мыслит экранами, а бэкенд — ресурсами.

Именно поэтому появился GraphQL — язык, который описывает, как клиент может запрашивать и структурировать данные с сервера.

Он решает три ключевые задачи:

1. Один endpoint — меньше связности между фронтендом и бэкендом.
2 Гибкий запрос — клиент сам определяет, какие данные ему нужны.
3. Типизированная схема — валидация, автодополнение и автогенерация документации.

На практике REST отлично подходит для стабильных эндпоинтов, а GraphQL для динамических вьюх и составных данных.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍4
Если ты вытягиваешь очень длинную строку типа NVARCHAR(MAX) через await ToListAsync(), Entity Framework начинает творить дичь = запрос становится медленным, а потребление памяти взлетает. Хотя, если быть точным, дело не в самом EF — основная работа ложится на Microsoft.Data.SqlClient. Проблема в том, что он неправильно использует SqlDataReader, из-за чего происходят лишние аллокации и запрос тормозит.

Апдейт: речь в основном про реализацию для SQL Server, так как она использует пакет Microsoft.Data.SqlClient. Чтобы понять, зацепила ли тебя эта проблема, просто глянь зависимости в проекте, есть ли там именно этот пакет.

Вот бенчмарк от Wraith2:

BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.2314)
.NET SDK 9.0.200
[Host] : .NET 8.0.13 (8.0.1325.6609), X64 RyuJIT
DefaultJob : .NET 8.0.13 (8.0.1325.6609), X64 RyuJIT


| Method | UseContinue | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|--------|--------------|----------:|----------:|----------:|----------:|----------:|----------:|----------:|
| Async | False | 757.51 ms | 15.053 ms | 36.642 ms | 2000.0000 | 1000.0000 | - | 101.49 MB |
| Sync | False | 39.40 ms | 0.543 ms | 0.508 ms | 2000.0000 | 888.8889 | 777.7778 | 80.14 MB |


Источник: https://github.com/dotnet/SqlClient/pull/3161

Он пофиксил эту проблему серией PR-ов, растянувшихся почти на пять лет. Это и круто, и немного безумно. Конечно, не пять лет подряд, но всё равно впечатляет.

Последние релиз-ноты можно почитать тут:
https://github.com/dotnet/SqlClient/blob/main/release-notes/6.1/6.1.0-preview1.md

Результаты такие:

| Method | UseContinue | Mean      | Error     | StdDev    | Gen0      | Gen1      | Gen2      | Allocated |
|--------|--------------|----------:|----------:|----------:|----------:|----------:|----------:|----------:|
| Async | True | 49.45 ms | 0.901 ms | 1.376 ms | 4333.3333 | 3555.5556 | 1111.1111 | 101.51 MB |
| Sync | True | 40.09 ms | 0.476 ms | 0.445 ms | 2000.0000 | 888.8889 | 777.7778 | 80.14 MB |


Остаётся примерно 20% разницы по аллокациям, но разрыв уже почти закрыт. Отличная работа от Wraith2 и всей команды. Можно протестировать новую версию пакета даже если ты работаешь через EF — прямые зависимости перекрывают транзитивные. Или просто подожди следующий релиз EF, где уже будет обновлённый Microsoft.Data.SqlClient.

Финальный стабильный релиз 6.1 уже совсем близко.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
7🤯6🔥4
C# Portal | Программирование
Каждое приложение, которое ты используешь, общается с сервером через API. Много лет стандартом был REST: - роуты, которые представляют ресурсы (/users, /posts) - классические методы: GET, POST, PUT, DELETE Работает отлично, но когда приложение разрастается…
Что происходит под капотом GraphQL-запроса

1) Клиент делает запрос

→ приложение отправляет запрос (HTTP POST) на эндпоинт /graphql
→ это один вызов, в котором клиент точно указывает, какие данные ему нужны

2) Сервер интерпретирует запрос

→ GraphQL парсит запрос и сверяет его со своей схемой — картой данных и связей между ними
→ если запрос не соответствует схеме (например, клиент запрашивает поле, которого бэкенд не отдает), запрос падает с ошибкой

3) Выполняются резолверы

→ у каждого поля в запросе есть функция, которая знает, как получить нужные данные
• user → ищет пользователя в базе данных
• posts → достает посты этого пользователя
→ GraphQL выполняет все эти функции и собирает результат в один ответ

4) Возврат ответа

→ сервер возвращает объект с той же структурой, что и исходный запрос
→ клиент получает ровно те данные, которые запросил, ничего лишнего

Клиент сам описывает, что ему нужно, а сервер знает, как это отдать

Вот это и есть GraphQL

На 2 иллюстрации хорошо видно, как GraphQL связывает всё воедино:

→ схема определяет, какие данные существуют и как они связаны
→ резолверы знают, откуда эти данные брать (база данных, API)
→ сервер собирает всё вместе и отдает клиенту единый ответ

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
3
Идемпотентность не зависит от HTTP-статусов. Она относится только к состоянию сервера.

Это довольно частое заблуждение у разработчиков: операция может оставаться идемпотентной, даже если сервер возвращает разные коды ответов. Главное, чтобы повторные запросы не меняли состояние системы.

Пример:

Запрос                            Ответ
DELETE /products/123 204
DELETE /products/123 404


Даже если второй запрос возвращает 404 (потому что ресурса уже нет), операция всё равно идемпотентна, при условии, что повторное удаление не вызывает новых побочных эффектов.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14
This media is not supported in your browser
VIEW IN TELEGRAM
Странный техно-эксперимент:

Парень использовал RayLib C# для генерации визуала, запускал всё в ASP.NET webhost, снимал пиксельные кадры, превращал их в HTML-div-ы как пиксели, и слал эти div-ы в браузер через Server-Sent Events.
Получилось что-то вроде стриминга гипермедиа-приложения или игры — без WebGL, без видео, чисто поток div-ов.

Безумие? Да. Гениально? Тоже да.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
14🥴7🔥3
Не выкатывай приложение, работающее с Kafka, в прод, без нагрузочного тестирования

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

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

Kafka-пайплайн может работать идеально в девелоперской среде, где трафик минимальный.
Но в проде неожиданные пики нагрузки легко вскрывают скрытые проблемы:

• проблемы с конкурентным доступом;
• неверные настройки для обработки backpressure;
• узкие места в I/O или сериализации при обработке сообщений;
• неправильное партиционирование, из-за которого нагрузка на консьюмеров распределяется неравномерно;
• неверно рассчитанное масштабирование числа консьюмеров под ожидаемую нагрузку;
• некорректное использование Kafka-транзакций, требующих координации;
• задержки commit-ack от нод.

В зависимости от твоего кейса, объёма данных, формата сообщений, сетевых условий, железа, конфигурации и качества кода, при работе с Kafka могут возникать разные проблемы:

• потеря или дублирование сообщений;
• перегрузка или падение брокеров;
• уязвимости или нарушения требований безопасности и комплаенса.

Для многих компаний скорость Kafka-пайплайна критична - особенно если она влияет на SLA.
Когда задаются SLO или SLA, латентность конкретных потоков должна оставаться стабильной, что делает задачу по обеспечению скорости и эффективности пайплайна ещё сложнее.

NBomber - мощный инструмент для симуляции реальной нагрузки на Kafka в .NET-приложениях.
Он позволяет тестировать продюсеров, консьюмеров и end-to-end флоу с помощью сценариев, написанных на C# или F#.

С NBomber можно:
• измерять полную латентность между продюсером и консьюмером;
• отслеживать, как меняется throughput при росте нагрузки;
• определять момент, когда консьюмер начинает отставать от продюсера.

Я использовал NBomber для нагрузочного тестирования боевого Kafka-пайплайна системы Fraud Detection, построенной на микросервисах.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
3
Я как кодер довольно щепетилен в разнице между цифрой 0 и буквой O.

В конце созвона саппорт продиктовал мне номер тикета:

четыре, один, восемь, T, A, два, «ОУ»

Зная, что обычные люди вечно путают, я уточнил:

«Это ноль, цифра? Или O, буква?»

(долгая пауза)

«Это ноль, буква.»

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
😁36🤯25🔥2
Разбор самой жёсткой уязвимости в .NET. Request smuggling и CVE-2025-55315

https://andrewlock.net/understanding-the-worst-dotnet-vulnerability-request-smuggling-and-cve-2025-55315/

В статье объяснили, что такое request smuggling, разбирают свежую дыру в AspNetCore с критичностью 9.9 и показывают, как атакеры могут это дело использовать.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
image_2025-10-29_09-13-40.png
2 MB
Проект на выходных: змейка на C#

Код получился кривоватый. Уверен, есть тысяча способов сделать это лучше. Я просто пытался сам допереть, как всё работает.

Немного олдскульной ностальгии по Nokia.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👏14🥴72👍2
Media is too big
VIEW IN TELEGRAM
Только что вышел Minecraftonia — воксельный движок на C# 13/.NET 9 и Avalonia. В проекте экспериментируют с кастомным воксельным рейтрейсингом, процедурной генерацией террейна и отзывчивым десктопным интерфейсом, при этом всё полностью кроссплатформенно.

Ссылка на релиз: https://github.com/wieslawsoltes/Minecraftonia/releases/tag/v0.1.0

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
😁15🔥14🥴3
В Entity Framework Core ToDictionaryAsync (и синхронный ToDictionary) вытягивают из базы весь объект целиком.

Если глянуть на сигнатуру ToDictionaryAsync:

public static Task<Dictionary<TKey, TElement>> ToDictionaryAsync<TSource, TKey, TElement>(
this IQueryable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector,
CancellationToken cancellationToken = default)
where TKey : notnull
=> ToDictionaryAsync(source, keySelector, elementSelector, comparer: null, cancellationToken);


Аргументы тут обычные лямбды (Func<TSource, ...>), а не выражения (Expression<Func<...>>). Значит, вычисление этих селекторов происходит на клиенте.

Допустим, у нас есть такой класс:

public class BlogPost
{
public int Id { get; set; }
public required string Title { get; set; }
public required string Description { get; set; }
public required string Content { get; set; }
public required string Author { get; set; }
}


И вот такой запрос:

return await dbContext.BlogPosts
.Where(...)
.ToDictionaryAsync(k => k.Author, v => v.Title);


Звучит красиво, но по факту EF притащит не только Author и Title, а вообще все поля, например, и Content тоже. На это есть issue, и я случайно наткнулся и офигел.

Как обойти проблему: сначала спроецировать данные через Select, это уже выполнится на стороне сервера:

return await dbContext.BlogPosts
.Where(...)
.Select(s => new { Author = s.Author, Title = s.Title })
.ToDictionaryAsync(k => k.Author, v => v.Title);


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
17🔥7🤔2
Мне очень нравится, что в C# 14 убрали лишнюю возню, связанную с полями, лежащими в основе свойств.

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

Ссылка: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14#the-field-keyword

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
15🔥12🥰2👍1
Перемещение Файлов и Папок в Корзину в .NET

При работе с файлами и папками в .NET-приложениях на Windows иногда нужно отправлять объекты в корзину, а не удалять их насовсем (File.Delete, Directory.Delete). Это позволяет пользователю восстановить случайно удалённые данные. Вот как сделать это через Windows Shell API:

static void MoveToRecycleBin(string path)
{
var shellType = Type.GetTypeFromProgID("Shell.Application", throwOnError: true)!;
dynamic shellApp = Activator.CreateInstance(shellType)!;

// https://learn.microsoft.com/en-us/windows/win32/api/shldisp/ne-shldisp-shellspecialfolderconstants?WT.mc_id=DT-MVP-5003978
var recycleBin = shellApp.Namespace(0xa);

// https://learn.microsoft.com/en-us/windows/win32/shell/folder-movehere?WT.mc_id=DT-MVP-5003978
recycleBin.MoveHere(path);
}


Этот метод подходит и для файлов, и для каталогов:

MoveToRecycleBin(@"C:\path\to\file.txt");
MoveToRecycleBin(@"C:\path\to\directory");


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍173🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
От чат‑бота к агентам: Microsoft Agent Framework в .NET

Microsoft опубликовала руководство по переходу с обычных чат-ботов на AI-агентов в .NET. В примерах показывают, как подключить Microsoft Agent Framework к AI-шаблонам, зарегистрировать агента в Program.cs, вынести поиск в инструмент через SearchAsync, настроить Azure OpenAI и векторное хранилище, а также включить телеметрию через OpenTelemetry и тестирование в .NET Aspire.

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

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
6🥴6🤨2👍1
Это может быть первым шагом при написании юнит-тестов.

Не всегда понятно, с чего начать. Но шаблон AAA почти всегда выручает.

// act ... код, который тестируем
// ДОЛЖЕН БЫТЬ ОДИН КОНКРЕТНЫЙ ВЫЗОВ

// arrange ... моки и подготовка окружения для act

// assert ... проверки результата act

И вот — у вас уже есть юнит-тест.

Дальше можно:
a) дублировать Test01 и менять под следующий кейс
b) параметризовать Test01, чтобы прогонять сразу несколько вариантов

В конце просто переименовать Test01..TestN во что-то осмысленное.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15