In-memory кэш в .NET: плюсы и минусы
✅ Плюсы:
• Очень быстрый
• Легко реализуется
• Не требует внешних зависимостей
⚠️ Минусы:
• Данные теряются при перезапуске
• Работает только в пределах одного инстанса
• Нет шаринга между приложениями
📌 Нужно больше — переходите на распределённый кэш.
Redis — топовый выбор, и в .NET есть два стабильных клиента:
•
•
📘 Хотите понять, когда и как использовать оба подхода?
Вот полный гайд:
https://milanjovanovic.tech/blog/caching-in-aspnetcore-improving-application-performance
✅ Плюсы:
• Очень быстрый
• Легко реализуется
• Не требует внешних зависимостей
⚠️ Минусы:
• Данные теряются при перезапуске
• Работает только в пределах одного инстанса
• Нет шаринга между приложениями
📌 Нужно больше — переходите на распределённый кэш.
Redis — топовый выбор, и в .NET есть два стабильных клиента:
•
StackExchange.Redis
•
Microsoft.Extensions.Caching.StackExchangeRedis
📘 Хотите понять, когда и как использовать оба подхода?
Вот полный гайд:
https://milanjovanovic.tech/blog/caching-in-aspnetcore-improving-application-performance
Дан код:
using System;
using System.Runtime.ExceptionServices;
class Program
{
static void Main()
{
try
{
Console.WriteLine(Foo());
}
catch (Exception ex)
{
Console.WriteLine($"Catch: {ex.Message}");
}
}
static int Foo()
{
try
{
return Bar();
}
finally
{
throw new Exception("finally in Foo");
}
}
static int Bar()
{
try
{
throw new Exception("error in Bar");
}
finally
{
Console.WriteLine("Bar finally executed");
}
}
}
Вопросы:
• Что напечатает программа и почему?
• В каком порядке выполняются блоки try / finally?
• Какая из двух ошибок дойдёт до catch, а какая потеряется?
• Как изменить код, чтобы не терять информацию о первой ошибке?
Разбор:
• Bar бросает Exception("error in Bar"). Прежде чем исключение покинет метод, выполняется его finally, выводя Bar finally executed.
• Управление переходит в Foo. Там срабатывает finally, который бросает новое исключение ("finally in Foo").
• По правилам CLR новое исключение из finally заменяет текущее. Первая ошибка пропадает.
• В Main перехватывается только «finally in Foo».
Консольный вывод:
Bar finally executed
Catch: finally in Foo
Как сохранить обе ошибки
Обернуть второе исключение так, чтобы первая причина не потерялась, например:
finally
{
var pending = new Exception("finally in Foo");
throw new AggregateException("Foo failed", pending);
}
или переслать исходное исключение через ExceptionDispatchInfo:
Exception pending = null;
static int Foo()
{
try
{
return Bar();
}
catch (Exception ex)
{
pending = ex; // сохраняем первую причину
throw;
}
finally
{
if (pending != null)
ExceptionDispatchInfo.Capture(pending).Throw();
}
}```
Так ни одна ошибка не будет потеряна, а отладка станет нагляднее.
Please open Telegram to view this post
VIEW IN TELEGRAM
Проблема: cортировка больших массивов может быть неэффективной при использовании простых алгоритмов, таких как сортировка пузырьком или вставками.
Решение: Автор в книге Algorithms and Data Structures for OOP With C# демонстрирует реализацию QuickSort — одного из самых эффективных алгоритмов сортировки на практике, с рекурсивным разбиением массива.
Пример кода:
public class QuickSortExample
{
public void QuickSort(int[] arr, int low, int high)
{
if (low < high)
{
int pi = Partition(arr, low, high);
QuickSort(arr, low, pi - 1);
QuickSort(arr, pi + 1, high);
}
}
private int Partition(int[] arr, int low, int high)
{
int pivot = arr[high];
int i = (low - 1);
for (int j = low; j < high; j++)
{
if (arr[j] < pivot)
{
i++;
(arr[i], arr[j]) = (arr[j], arr[i]);
}
}
(arr[i + 1], arr[high]) = (arr[high], arr[i + 1]);
return i + 1;
}
}
Преимущества:
— Быстрая сортировка даже больших наборов данных
— Средняя сложность O(n log n)
— Эффективное использование памяти за счет рекурсии
Please open Telegram to view this post
VIEW IN TELEGRAM
Трюк: добавление элементов в словарь со списками через `??=`
Разбор:
- При обращении к dict[key] отсутствующий ключ вернёт default(List<int>), то есть null.
- Оператор ??= проверяет левую часть на null и, если она null, присваивает справа новое значение.
- В нашем случае, если dict[key] был null, создаётся новый List<int> и сразу сохраняется в словаре.
- После этого метод .Add(item) вызывается уже на существующем списке.
- В результате за одну строчку мы и проверили наличие, и создали новый список при необходимости, и добавили элемент.
var dict = new Dictionary<string, List<int>>();
string key = "numbers";
int item = 42;
// Обычный способ:
if (!dict.TryGetValue(key, out var list))
{
list = new List<int>();
dict[key] = list;
}
list.Add(item);
// Короткий трюк:
(dict[key] ??= new List<int>()).Add(item);
Разбор:
- При обращении к dict[key] отсутствующий ключ вернёт default(List<int>), то есть null.
- Оператор ??= проверяет левую часть на null и, если она null, присваивает справа новое значение.
- В нашем случае, если dict[key] был null, создаётся новый List<int> и сразу сохраняется в словаре.
- После этого метод .Add(item) вызывается уже на существующем списке.
- В результате за одну строчку мы и проверили наличие, и создали новый список при необходимости, и добавили элемент.
🎯 Using .NET Aspire With the Docker Publisher — практическое руководство от Milan Jovanović
.NET Aspire — это современный фреймворк от Microsoft для создания облачных микросервисов. В статье показано, как автоматически интегрировать Docker Compose в .NET‑приложение с помощью нового инструмента — Aspire Docker Publisher.
✅ Что вы узнаете:
1. Как описать окружение прямо в C#:
2. Как опубликовать проект:
После чего автоматически создаётся
3. Что входит в результат:
- Готовый
- Поддержка портов, переменных среды, volume и сетей
- Полная инфраструктура, которую можно деплоить хоть на VPS
4. Как это работает на проде:
- Всё, что нужно: скопировать артефакты →
- Можно легко обернуть через Nginx или Traefik, подключить SSL
🧠 Почему это удобно:
- Не нужно вручную писать YAML — всё в коде
- Повышается воспроизводимость и читаемость инфраструктуры
- Упрощает переход от локальной разработки к боевому деплою
🔗 Статья: www.milanjovanovic.tech/blog/using-dotnet-aspire-with-the-docker-publisher
.NET Aspire — это современный фреймворк от Microsoft для создания облачных микросервисов. В статье показано, как автоматически интегрировать Docker Compose в .NET‑приложение с помощью нового инструмента — Aspire Docker Publisher.
✅ Что вы узнаете:
1. Как описать окружение прямо в C#:
builder.AddDockerComposeEnvironment("aspire-docker-demo");
var postgres = builder.AddPostgres("database").WithDataVolume();
var redis = builder.AddRedis("cache");
var webApi = builder.AddProject<Projects.Web_Api>("web-api")
.WithReference(postgres).WaitFor(postgres)
.WithReference(redis).WaitFor(redis);
builder.Build().Run();
2. Как опубликовать проект:
dotnet tool install --global aspire.cli --prerelease
aspire publish -o docker-compose-artifacts
После чего автоматически создаётся
docker-compose.yml
и .env
.3. Что входит в результат:
- Готовый
docker-compose.yml
со всеми зависимостями- Поддержка портов, переменных среды, volume и сетей
- Полная инфраструктура, которую можно деплоить хоть на VPS
4. Как это работает на проде:
- Всё, что нужно: скопировать артефакты →
docker compose up -d
- Можно легко обернуть через Nginx или Traefik, подключить SSL
🧠 Почему это удобно:
- Не нужно вручную писать YAML — всё в коде
- Повышается воспроизводимость и читаемость инфраструктуры
- Упрощает переход от локальной разработки к боевому деплою
🔗 Статья: www.milanjovanovic.tech/blog/using-dotnet-aspire-with-the-docker-publisher
🧠 .NET-задача для продвинутых: потокобезопасная очередь с приоритетами и отменой задач
Задача:
Реализуйте асинхронный планировщик задач с приоритетами и возможностью отмены. Требования:
1. Есть очередь задач (`Func<CancellationToken, Task>`), каждая с целочисленным приоритетом (0 — самый высокий).
2. Задачи выполняются параллельно, но не более
3. Если приходит задача с более высоким приоритетом, и нет свободных слотов, она может вытеснить задачу с самым низким приоритетом.
4. Вытеснённая задача должна быть отменена (через `CancellationToken`), и её ресурсы — корректно освобождены.
5. Код должен быть потокобезопасным и устойчивым к гонкам.
Дополнительно:
- Используйте
- Не допускайте deadlock’ов
- Обеспечьте корректное завершение планировщика по команде
Пример API:
@csharp_1001_notes
Задача:
Реализуйте асинхронный планировщик задач с приоритетами и возможностью отмены. Требования:
1. Есть очередь задач (`Func<CancellationToken, Task>`), каждая с целочисленным приоритетом (0 — самый высокий).
2. Задачи выполняются параллельно, но не более
N
одновременно.3. Если приходит задача с более высоким приоритетом, и нет свободных слотов, она может вытеснить задачу с самым низким приоритетом.
4. Вытеснённая задача должна быть отменена (через `CancellationToken`), и её ресурсы — корректно освобождены.
5. Код должен быть потокобезопасным и устойчивым к гонкам.
Дополнительно:
- Используйте
PriorityQueue
, SemaphoreSlim
, CancellationTokenSource
- Не допускайте deadlock’ов
- Обеспечьте корректное завершение планировщика по команде
StopAsync()
Пример API:
public class PriorityTaskScheduler
{
public PriorityTaskScheduler(int maxParallelism);
public Task EnqueueAsync(Func<CancellationToken, Task> task, int priority);
public Task StopAsync();
}
@csharp_1001_notes
⚠️ Как вы обрабатываете ошибки в C#?
Многие используют исключения для управления потоком и быстрого фейла. Но в C# метод не сообщает, какие именно исключения он может выбросить — это не видно в сигнатуре.
🔍 Мой подход:
Исключения — только для исключительных ситуаций.
Если метод может ожидаемо провалиться, пусть это будет явно.
✅ Используйте Result-паттерн:
— Метод возвращает
— Caller обязан проверить
— Код становится предсказуемее и легче тестируется
— Дополнительно: пропускная способность может быть выше, чем при
Пример:
Подробнее
Многие используют исключения для управления потоком и быстрого фейла. Но в C# метод не сообщает, какие именно исключения он может выбросить — это не видно в сигнатуре.
🔍 Мой подход:
Исключения — только для исключительных ситуаций.
Если метод может ожидаемо провалиться, пусть это будет явно.
✅ Используйте Result-паттерн:
— Метод возвращает
Result<T>
вместо выбрасывания исключения — Caller обязан проверить
IsSuccess
и обработать ошибку — Код становится предсказуемее и легче тестируется
— Дополнительно: пропускная способность может быть выше, чем при
throw/catch
Пример:
Result<User> result = userService.FindById(id);
if (!result.IsSuccess)
return Error(result.Error);
Подробнее
Если ты пишешь на .NET — возможно, ты хотя бы раз совершал одну из этих архитектурных (или просто утомительных) ошибок. Вот список анти-паттернов.
1. Blazor вместо React
Переизобретать веб на C# ради UI? Ты не Google. Если нужен зрелый фронт, бери то, что уже доказало свою масштабируемость.
2. Serverless Azure Functions на одном App Service Plan
"О, это же серверлесс!" — пока не замечаешь, что всё сидит на одном сервисе. Масштабируемость мнимая, расходы настоящие.
3. Самодельный MVC поверх Minimal APIs
Зачем ты пишешь свою обвязку маршрутов, контроллеров и зависимостей, если MVC уже всё это умеет?
4. Хранимки для CRUD
Хранимки — не антипаттерн, но когда их 200 штук для банального Insert/Update/Delete — это просто ORM с человеческим лицом, только сложнее.
5. "Юнит-тесты", которым нужен деплой БД
Это не юнит-тесты. Это интеграционные, а если быть честным — "fragile-тесты".
6. AutoMapper
В теории красиво, в проде — «а почему здесь null?». Иногда проще написать руками и понять, что происходит.
7. Игнор Microsoft Orleans
Если делаешь real-time, pub/sub, воркеры — Orleans стоит хотя бы попробовать. Он закрывает много боли, которую ты иначе сам будешь собирать по кускам.
8. Начинать с "микросервисов"
Монолит — не зло. Зло — это 12 проектов в solution, которые общаются через HTTP и собираются 10 минут.
9. gRPC для фронта
Если у тебя браузер → JS → gRPC → base64 → JSON — просто остановись. Возьми REST или GraphQL и живи спокойно.
10. Razor Pages вместо MVC
Да, они проще на старте. Но когда проект растёт, ты захочешь нормальное разделение по слоям, маршрутам и структуре.
🧠 Вывод: не все возможности .NET надо использовать. Иногда сила — в простоте.
C чем согласны ? Что добавили бы ?
#dotnet #csharp #webdev #архитектура #программирование
Please open Telegram to view this post
VIEW IN TELEGRAM
🔐 Блог DevelopersVoice выпустил отличный гайд по **10 главным уязвимостям веб‑приложений с примерами на .NET.
Что внутри:
• Инъекции и XSS
• Ошибки аутентификации
• Уязвимые зависимости
• SSRF и плохая конфигурация
• Проблемы с логированием и безопасным дизайном
📌 Всё с практическими советами: как обнаружить, как исправить, как не допустить.
Полный гайд тут: https://developersvoice.com/blog/secure-coding/owasp-top-ten
#OWASP #SecureCoding #DotNet #WebSecurity #DevTips
Что внутри:
• Инъекции и XSS
• Ошибки аутентификации
• Уязвимые зависимости
• SSRF и плохая конфигурация
• Проблемы с логированием и безопасным дизайном
📌 Всё с практическими советами: как обнаружить, как исправить, как не допустить.
Полный гайд тут: https://developersvoice.com/blog/secure-coding/owasp-top-ten
#OWASP #SecureCoding #DotNet #WebSecurity #DevTips
📌 PolySharp — удобный способ использовать новые фичи C# на старых версиях .NET. Этот NuGet-пакет работает как source-генератор, автоматически подбирая нужные полифиллы в зависимости от целевой платформы. Для работы достаточно добавить ссылку на PolySharp, установить последнюю версию C# и можно писать современный код даже для .NET Framework или UWP.
Инструмент обладает умной генерацией только необходимых типов. Например, если компилятору C# 13 нужен
🤖 GitHub
@csharp_ci
Инструмент обладает умной генерацией только необходимых типов. Например, если компилятору C# 13 нужен
[IsExternalInit]
для init-only свойств, PolySharp создаст его за кулисами. При этом он не трогает фичи, требующие поддержки рантайма, но покрывает огромный пласт синтаксических улучшений — от nullable-аннотаций до интерполированных строковых обработчиков. 🤖 GitHub
@csharp_ci
Какое исключение выдается, если протокол, поддерживаемый префиксом URI, недействителен?
Anonymous Quiz
10%
URLNotFound
29%
NotSupportedException
48%
UriFormatException
12%
URLSourceNotFound
🔥 .NET Aspire: как упростить Service Discovery в микросервисах
Вместо ручной настройки адресов и портов, фреймворк .NET Aspire позволяет упростить обнаружение сервисов с помощью декларативного подхода.
🔹 Конфигурация через
🔹 Сервисы автоматически "обнаруживаются" и подключаются
🔹 Поддержка локальной разработки, контейнеров и облака
🔹 Логи, метрики, health checks — встроены
📌 Aspire — это не просто упрощение разработки, это фундамент для масштабируемых .NET-приложений.
🧠 Полный разбор — в блоге:
[how-dotnet-aspire-simplifies-service-discovery](https://www.milanjovanovic.tech/blog/how-dotnet-aspire-simplifies-service-discovery)
Подпишись, чтобы не пропускать важные новинки .NET и облачной разработки.
Вместо ручной настройки адресов и портов, фреймворк .NET Aspire позволяет упростить обнаружение сервисов с помощью декларативного подхода.
🔹 Конфигурация через
.AddProject()
и .WithReference()
🔹 Сервисы автоматически "обнаруживаются" и подключаются
🔹 Поддержка локальной разработки, контейнеров и облака
🔹 Логи, метрики, health checks — встроены
📌 Aspire — это не просто упрощение разработки, это фундамент для масштабируемых .NET-приложений.
🧠 Полный разбор — в блоге:
[how-dotnet-aspire-simplifies-service-discovery](https://www.milanjovanovic.tech/blog/how-dotnet-aspire-simplifies-service-discovery)
Подпишись, чтобы не пропускать важные новинки .NET и облачной разработки.
😈 Хитрая задачка на C# — замыкания и ловушка в цикле
Что выведет этот код?
На первый взгляд кажется, что будет:
Но на самом деле вывод:
💡 Почему?
Все лямбды замкнулись на одну и ту же переменную i, и когда они выполняются — i уже стало 5.
✅ Как исправить:
Теперь всё работает как ожидается.
🧠 Замыкания в C# захватывают переменные, а не их значения! Аккуратнее с циклами и лямбдами.
Что выведет этот код?
var actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
actions.Add(() => Console.WriteLine(i));
}
foreach (var action in actions)
{
action();
}
На первый взгляд кажется, что будет:
0
1
2
3
4
Но на самом деле вывод:
5
5
5
5
5
💡 Почему?
Все лямбды замкнулись на одну и ту же переменную i, и когда они выполняются — i уже стало 5.
✅ Как исправить:
for (int i = 0; i < 5; i++)
{
int copy = i;
actions.Add(() => Console.WriteLine(copy));
}
Теперь всё работает как ожидается.
🧠 Замыкания в C# захватывают переменные, а не их значения! Аккуратнее с циклами и лямбдами.
Почти каждый разработчик допускал эту ошибку.
Что не так с этим кодом?
На первый взгляд всё кажется логичным:
🔸 API-эндпоинт регистрации пользователя вызывает
🔸
🔸 EmailService через
Но если присмотреться, метод
В чём проблема с
Вот суть:
❌
Если внутри
Вместо этого приложение может тихо упасть или начать вести себя непредсказуемо.
Почему так происходит?
Методы
Исключения из async void проходят мимо стандартных механизмов обработки.
Правильный подход:
✅ Всегда возвращай Task
Запомни:
Что не так с этим кодом?
На первый взгляд всё кажется логичным:
UserService
UserService
сохраняет пользователя в базу и вызывает EmailService
SmtpClient
отправляет письмоНо если присмотреться, метод
SendWelcomeEmail
объявлен как async void
.В чём проблема с
async void
?Вот суть:
async void
делает невозможным отлов исключений.Если внутри
SendEmailAsync()
произойдёт исключение — catch
его не перехватит.Вместо этого приложение может тихо упасть или начать вести себя непредсказуемо.
Почему так происходит?
Методы
async void
не возвращают Task, поэтому вызывающий код не может их await-ить и обрабатывать ошибки.Исключения из async void проходят мимо стандартных механизмов обработки.
Правильный подход:
Запомни:
async void
допустим только для обработчиков событий, где возвращаемый void обязателен.Please open Telegram to view this post
VIEW IN TELEGRAM
Правильный способ построения асинхронных API в .NET
Большинство API работают по простому шаблону:
🔸 Клиент отправляет запрос
🔸 Сервер выполняет работу
🔸 Сервер возвращает ответ
Такой подход отлично работает для быстрых операций — например, получения данных или простых обновлений.
А что насчёт длительных операций?
Речь о задачах вроде:
> обработки больших файлов
> генерации отчётов
> конвертации видео
Такие процессы могут занимать от нескольких минут до часов.
Вот как можно правильно строить асинхронные API: читать
Как сообщить клиенту, что его запрос обработан?
Есть два подхода:
🔸 PULL — клиент опрашивает API, чтобы узнать статус
🔸 PUSH — сервер сам уведомляет клиента (через WebSocket, email и т.д.)
Большинство API работают по простому шаблону:
Такой подход отлично работает для быстрых операций — например, получения данных или простых обновлений.
А что насчёт длительных операций?
Речь о задачах вроде:
> обработки больших файлов
> генерации отчётов
> конвертации видео
Такие процессы могут занимать от нескольких минут до часов.
Вот как можно правильно строить асинхронные API: читать
Как сообщить клиенту, что его запрос обработан?
Есть два подхода:
Please open Telegram to view this post
VIEW IN TELEGRAM
🧠 Задача: "Сколько раз выполнится блок Console.WriteLine?"
❓Вопрос:
Что выведет программа?
🔍 Разбор:
На первый взгляд кажется, что программа выведет:
1
2
3
📎 Но на самом деле она выведет:
0
1
2
Почему?
Оператор obj++ — это постфиксный инкремент. Он:
сначала вызывает implicit operator int для текущего значения,
а затем вызывает operator ++.
То есть порядок такой:
Console.WriteLine(obj++) вызывает implicit int до инкремента
только потом ++ увеличивает значение.
✅ Что это проверяет:
Знание порядка вызова операторов (++ и implicit)
Понимание поведения постфиксных операций в .NET
Умение читать и анализировать перегрузки операторов
@csharp_1001_notes
public class Program
{
public static void Main()
{
var obj = new Counter();
for (int i = 0; i < 3; i++)
{
Console.WriteLine(obj++);
}
}
}
public class Counter
{
private int _value = 0;
public static Counter operator ++(Counter c)
{
c._value++;
return c;
}
public static implicit operator int(Counter c)
{
return c._value;
}
}
❓Вопрос:
Что выведет программа?
🔍 Разбор:
1
2
3
📎 Но на самом деле она выведет:
0
1
2
Почему?
Оператор obj++ — это постфиксный инкремент. Он:
сначала вызывает implicit operator int для текущего значения,
а затем вызывает operator ++.
То есть порядок такой:
Console.WriteLine(obj++) вызывает implicit int до инкремента
только потом ++ увеличивает значение.
✅ Что это проверяет:
Знание порядка вызова операторов (++ и implicit)
Понимание поведения постфиксных операций в .NET
Умение читать и анализировать перегрузки операторов
@csharp_1001_notes
🔥 𝟮𝟬 Полезных статей для изучения ASP.NET Core
1. Как заменить исключения на Result-паттерн
↳ https://antondevtips.com/blog/how-to-replace-exceptions-with-result-pattern-in-dotnet
2. Как логировать запросы и ответы API
↳ https://antondevtips.com/blog/logging-requests-and-responses-for-api-requests-and-httpclient-in-aspnetcore
3. Как реализовать refresh-токены и отзыв токенов
↳ https://antondevtips.com/blog/how-to-implement-refresh-tokens-and-token-revocation-in-aspnetcore
4. Как создавать и конвертировать PDF-документы
↳ https://antondevtips.com/blog/how-to-create-and-convert-pdf-documents-in-aspnetcore
5. Как писать собственные middleware
↳ https://antondevtips.com/blog/how-to-create-custom-middlewares-in-asp-net-core
6. Серверные события в реальном времени (SSE)
↳ https://antondevtips.com/blog/real-time-server-sent-events-in-asp-net-core
7. Лучшие практики аутентификации и авторизации
↳ https://antondevtips.com/blog/authentication-and-authorization-best-practices-in-aspnetcore
8. Лучший способ маппинга объектов
↳ https://antondevtips.com/blog/the-best-way-to-map-objects-in-dotnet-in-2024
9. Лучший способ валидации объектов
↳ https://antondevtips.com/blog/the-best-way-to-validate-objects-in-dotnet-in-2024
10. Паттерн Options и работа с конфигурацией
↳ https://antondevtips.com/blog/master-configuration-in-asp-net-core-with-the-options-pattern
11. Как повысить производительность Web API
↳ https://antondevtips.com/blog/how-to-increase-performance-of-web-apis-in-dotnet
12. 15 типичных ошибок при создании Web API
↳ https://antondevtips.com/blog/top-15-mistakes-developers-make-when-creating-web-apis
13. Как реализовать кэширование
↳ https://antondevtips.com/blog/how-to-implement-caching-strategies-in-dotnet
14. Быстрый старт с FastEndpoints
↳ https://antondevtips.com/blog/getting-started-with-fastendpoints-for-building-web-apis-in-dotnet
15. Деплой приложений .NET в Azure через Aspire
↳ https://antondevtips.com/blog/how-to-deploy-dotnet-application-to-azure-using-neon-postgres-and-dotnet-aspire
16. 15 ошибок, которые совершают .NET-разработчики
↳ https://antondevtips.com/blog/top-15-mistakes-dotnet-developers-make-how-to-avoid-common-pitfalls
17. Лучшие практики повышения качества кода
↳ https://antondevtips.com/blog/best-practices-for-increasing-code-quality-in-dotnet-projects
18. Как создать мультиарендное приложение
↳ https://antondevtips.com/blog/how-to-implement-multitenancy-in-asp-net-core-with-ef-core
19. Архитектура на основе вертикальных срезов
↳ https://antondevtips.com/blog/vertical-slice-architecture-the-best-ways-to-structure-your-project
20. Как структурировать проект с Clean Architecture + Vertical Slices
↳ https://antondevtips.com/blog/the-best-way-to-structure-your-dotnet-projects-with-clean-architecture-and-vertical-slices
📥 Скачать дорожную карту по ASP.NET Core на 2025 год:
↳ https://antondevtips.com/roadmap/aspnetcore?utm_source=linkedin&utm_medium=social&utm_campaign=aspnetcore-roadmap-2025
1. Как заменить исключения на Result-паттерн
↳ https://antondevtips.com/blog/how-to-replace-exceptions-with-result-pattern-in-dotnet
2. Как логировать запросы и ответы API
↳ https://antondevtips.com/blog/logging-requests-and-responses-for-api-requests-and-httpclient-in-aspnetcore
3. Как реализовать refresh-токены и отзыв токенов
↳ https://antondevtips.com/blog/how-to-implement-refresh-tokens-and-token-revocation-in-aspnetcore
4. Как создавать и конвертировать PDF-документы
↳ https://antondevtips.com/blog/how-to-create-and-convert-pdf-documents-in-aspnetcore
5. Как писать собственные middleware
↳ https://antondevtips.com/blog/how-to-create-custom-middlewares-in-asp-net-core
6. Серверные события в реальном времени (SSE)
↳ https://antondevtips.com/blog/real-time-server-sent-events-in-asp-net-core
7. Лучшие практики аутентификации и авторизации
↳ https://antondevtips.com/blog/authentication-and-authorization-best-practices-in-aspnetcore
8. Лучший способ маппинга объектов
↳ https://antondevtips.com/blog/the-best-way-to-map-objects-in-dotnet-in-2024
9. Лучший способ валидации объектов
↳ https://antondevtips.com/blog/the-best-way-to-validate-objects-in-dotnet-in-2024
10. Паттерн Options и работа с конфигурацией
↳ https://antondevtips.com/blog/master-configuration-in-asp-net-core-with-the-options-pattern
11. Как повысить производительность Web API
↳ https://antondevtips.com/blog/how-to-increase-performance-of-web-apis-in-dotnet
12. 15 типичных ошибок при создании Web API
↳ https://antondevtips.com/blog/top-15-mistakes-developers-make-when-creating-web-apis
13. Как реализовать кэширование
↳ https://antondevtips.com/blog/how-to-implement-caching-strategies-in-dotnet
14. Быстрый старт с FastEndpoints
↳ https://antondevtips.com/blog/getting-started-with-fastendpoints-for-building-web-apis-in-dotnet
15. Деплой приложений .NET в Azure через Aspire
↳ https://antondevtips.com/blog/how-to-deploy-dotnet-application-to-azure-using-neon-postgres-and-dotnet-aspire
16. 15 ошибок, которые совершают .NET-разработчики
↳ https://antondevtips.com/blog/top-15-mistakes-dotnet-developers-make-how-to-avoid-common-pitfalls
17. Лучшие практики повышения качества кода
↳ https://antondevtips.com/blog/best-practices-for-increasing-code-quality-in-dotnet-projects
18. Как создать мультиарендное приложение
↳ https://antondevtips.com/blog/how-to-implement-multitenancy-in-asp-net-core-with-ef-core
19. Архитектура на основе вертикальных срезов
↳ https://antondevtips.com/blog/vertical-slice-architecture-the-best-ways-to-structure-your-project
20. Как структурировать проект с Clean Architecture + Vertical Slices
↳ https://antondevtips.com/blog/the-best-way-to-structure-your-dotnet-projects-with-clean-architecture-and-vertical-slices
📥 Скачать дорожную карту по ASP.NET Core на 2025 год:
↳ https://antondevtips.com/roadmap/aspnetcore?utm_source=linkedin&utm_medium=social&utm_campaign=aspnetcore-roadmap-2025
🧼 Как элегантно работать с конфигами в .NET?
Раньше я просто тянул значения напрямую из
Это быстро надоедает: много повторений, легко ошибиться, сложно валидировать.
Сейчас всегда использую Options pattern — и вот почему:
✅ Привязываешь конфигурацию к строго типизированным классам
✅ Добавляешь валидацию через аннотации
✅ Чисто внедряешь настройки через DI (`IOptions<T>` или `IOptionsSnapshot<T>`)
Отлично работает для:
-
- фичефлагов
- конфигов внешних сервисов
Если хочешь чистый и масштабируемый код — лучше подхода пока нет.
Раньше я просто тянул значения напрямую из
IConfiguration
. Это быстро надоедает: много повторений, легко ошибиться, сложно валидировать.
Сейчас всегда использую Options pattern — и вот почему:
✅ Привязываешь конфигурацию к строго типизированным классам
✅ Добавляешь валидацию через аннотации
✅ Чисто внедряешь настройки через DI (`IOptions<T>` или `IOptionsSnapshot<T>`)
Отлично работает для:
-
appsettings.json
- фичефлагов
- конфигов внешних сервисов
Если хочешь чистый и масштабируемый код — лучше подхода пока нет.