💡 Soft delete в EF Core без лишней логики в сервисах
Удалять данные физически — не всегда хорошая идея.
Логи, аудит, восстановление, аналитика — всё это требует soft delete.
Вот удобный способ реализовать его через EF Core interceptor.
Что делает перехватчик:
- Проверяет
- Если состояние сущности —
- Меняет его на
- Устанавливает:
-
-
В итоге:
Вы вызываете обычный:
А в базе:
- запись не удаляется
- просто помечается как удалённая
Плюсы подхода:
- никакой логики soft delete в сервисах и репозиториях
- единая точка обработки
- чистый доменный код
- безопасное удаление по всему приложению
Важно:
Если у вас есть связанные сущности (navigation properties),
перехватчик нужно дополнительно расширить — каскадное soft-удаление EF Core не делает автоматически.
Soft delete через interceptor — это один из самых чистых production-подходов для EF Core.
#dotnet #EFCore #Backend #Architecture #CSharp
Удалять данные физически — не всегда хорошая идея.
Логи, аудит, восстановление, аналитика — всё это требует soft delete.
Вот удобный способ реализовать его через EF Core interceptor.
Что делает перехватчик:
- Проверяет
ChangeTracker на сущности с интерфейсом ISoftDeletable- Если состояние сущности —
Deleted- Меняет его на
Modified- Устанавливает:
-
IsDeleted = true-
DeletedOnUtc = DateTime.UtcNowВ итоге:
Вы вызываете обычный:
context.Remove(entity);
А в базе:
- запись не удаляется
- просто помечается как удалённая
Плюсы подхода:
- никакой логики soft delete в сервисах и репозиториях
- единая точка обработки
- чистый доменный код
- безопасное удаление по всему приложению
Важно:
Если у вас есть связанные сущности (navigation properties),
перехватчик нужно дополнительно расширить — каскадное soft-удаление EF Core не делает автоматически.
Soft delete через interceptor — это один из самых чистых production-подходов для EF Core.
#dotnet #EFCore #Backend #Architecture #CSharp
Без событий сервисы быстро превращаются в «комбайн»:
— бизнес-логика
— отправка email
— аналитика
— интеграции
— и всё в одном методе
Каждая новая функция добавляет прямые зависимости.
Метод растёт. Связанность увеличивается. Поддержка усложняется.
С Domain Events подход меняется:
Вы выполняете бизнес-действие и просто публикуете событие.
await _domainEventsDispatcher.DispatchAsync(
[new UserRegisteredDomainEvent(user.Id, user.Email)]
);
Дальше система реагирует сама:
- отправка приветственного письма
- трекинг в аналитике
- интеграции
- любые новые сценарии
И всё это без изменения
UserService.Преимущества:
- никаких жёстких зависимостей
- сервисы меньше и чище
- легче тестировать
- проще масштабировать
- новые фичи добавляются без переписывания существующего кода
Публикуешь один раз - система расширяется сама.
Please open Telegram to view this post
VIEW IN TELEGRAM
⚡️ Exceptions - это для действительно исключительных ситуаций.
Но в реальном приложении они все равно будут случаться, и их нужно обрабатывать предсказуемо.
Самый удобный способ в ASP.NET Core - сделать глобальный обработчик через middleware:
- перехватываем любые необработанные исключения
- логируем ошибку
- возвращаем клиенту единый JSON-ответ (ProblemDetails) с 500
Пример:
Но в реальном приложении они все равно будут случаться, и их нужно обрабатывать предсказуемо.
Самый удобный способ в ASP.NET Core - сделать глобальный обработчик через middleware:
- перехватываем любые необработанные исключения
- логируем ошибку
- возвращаем клиенту единый JSON-ответ (ProblemDetails) с 500
Пример:
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
public ExceptionHandlingMiddleware(
RequestDelegate next,
ILogger<ExceptionHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception exception)
{
_logger.LogError(
exception, "Exception occurred: {Message}", exception.Message);
var problemDetails = new ProblemDetails
{
Status = StatusCodes.Status500InternalServerError,
Title = "Server Error"
};
context.Response.StatusCode =
StatusCodes.Status500InternalServerError;
await context.Response.WriteAsJsonAsync(problemDetails);
}
}
}
В .NET 8 появился простой способ сделать HttpClient устойчивым к сбоям — буквально одной строкой.
Microsoft добавила библиотеку Microsoft.Extensions.Http.Resilience, в которой уже есть готовые pipeline’ы для обработки ошибок при HTTP-запросах.
Что это даёт из коробки:
- Retry при временных сбоях
- Timeout
- Circuit Breaker
- Rate limiting
- Защиту от перегрузки
Подключается максимально просто:
Microsoft добавила библиотеку Microsoft.Extensions.Http.Resilience, в которой уже есть готовые pipeline’ы для обработки ошибок при HTTP-запросах.
Что это даёт из коробки:
- Retry при временных сбоях
- Timeout
- Circuit Breaker
- Rate limiting
- Защиту от перегрузки
Подключается максимально просто:
services.AddHttpClient<GitHubService>(static httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
})
.AddStandardResilienceHandler();
Как меняется роль разработчика в 2026 году?
AI стремительно меняет то, как создаются продукты и пишется код. Всё чаще обсуждают не только новые инструменты, но и более фундаментальный вопрос: какой будет роль разработчика в ближайшие годы.
24 марта Mindbox проводит открытые дебаты, где эксперты по AI из SberDevices и Itsy обсудят, куда движутся технологии и как это влияет на работу инженеров.
Что будет на встрече:
— разговор о том, как на AI смотрят с двух сторон: со стороны бизнеса и со стороны разработки
— возможность задать свои вопросы и обсудить их прямо во время дебатов
— реальные идеи и практики, которые уже используют команды
Если вам интересно, как меняется индустрия и какие навыки будут важны дальше — подключайтесь и приносите свои вопросы.
Канал Сергея Маркова
Канал Никиты Архипова
📅 24 марта
⏰ 19:00–20:30 мск
📍 Онлайн, бесплатно
👉 Зарегистрироваться
Реклама. ООО «Майндбокс», ИНН: 7713688880, erid: 2W5zFGn2zp1
AI стремительно меняет то, как создаются продукты и пишется код. Всё чаще обсуждают не только новые инструменты, но и более фундаментальный вопрос: какой будет роль разработчика в ближайшие годы.
24 марта Mindbox проводит открытые дебаты, где эксперты по AI из SberDevices и Itsy обсудят, куда движутся технологии и как это влияет на работу инженеров.
Что будет на встрече:
— разговор о том, как на AI смотрят с двух сторон: со стороны бизнеса и со стороны разработки
— возможность задать свои вопросы и обсудить их прямо во время дебатов
— реальные идеи и практики, которые уже используют команды
Если вам интересно, как меняется индустрия и какие навыки будут важны дальше — подключайтесь и приносите свои вопросы.
Канал Сергея Маркова
Канал Никиты Архипова
📅 24 марта
⏰ 19:00–20:30 мск
📍 Онлайн, бесплатно
👉 Зарегистрироваться
Реклама. ООО «Майндбокс», ИНН: 7713688880, erid: 2W5zFGn2zp1
C# records - идеальный способ создавать value objects.
Почему это важно?
В DDD value object должен:
- быть неизменяемым (immutable)
- сравниваться по значениям, а не по ссылке
Именно это records дают из коробки.
Что получаем:
- неизменяемость через
- структурное сравнение (Value Equality)
- короткий и чистый синтаксис
- deconstruction
- pattern matching
Пример:
Теперь два Address считаются равными, если совпадают их поля - без переопределения Equals и GetHashCode.
Раньше для value object приходилось писать:
• конструкторы
• Equals
• GetHashCode
• оператор ==
Теперь - одна строка.
Records делают value objects:
• безопаснее
• проще
• ближе к функциональному стилю
Если используете DDD в .NET - records должны быть стандартом для value objects.
Почему это важно?
В DDD value object должен:
- быть неизменяемым (immutable)
- сравниваться по значениям, а не по ссылке
Именно это records дают из коробки.
Что получаем:
- неизменяемость через
init- структурное сравнение (Value Equality)
- короткий и чистый синтаксис
- deconstruction
- pattern matching
Пример:
public class Booking
{
public Address Address { get; init; }
public DateRange Period { get; init; }
}
public record Address(
string Street,
string City,
string State,
string Country,
string ZipCode);
Теперь два Address считаются равными, если совпадают их поля - без переопределения Equals и GetHashCode.
Раньше для value object приходилось писать:
• конструкторы
• Equals
• GetHashCode
• оператор ==
Теперь - одна строка.
Records делают value objects:
• безопаснее
• проще
• ближе к функциональному стилю
Если используете DDD в .NET - records должны быть стандартом для value objects.
Есть более эффективный способ делать массовые обновления в EF Core.
Начиная с EF Core 7 появился метод ExecuteUpdate, который позволяет обновлять записи напрямую на стороне базы данных.
Вместо того чтобы загружать объекты в память, изменять их в цикле и затем вызывать
Это значительно быстрее и снижает нагрузку на приложение, особенно при работе с большими таблицами.
Однако есть важное условие: нужно заранее знать, какое поле вы обновляете и какое значение ему присваиваете, поскольку операция формируется как прямой SQL-update.
Такой подход отлично подходит для массовых операций и оптимизации производительности в EF Core.
Начиная с EF Core 7 появился метод ExecuteUpdate, который позволяет обновлять записи напрямую на стороне базы данных.
Вместо того чтобы загружать объекты в память, изменять их в цикле и затем вызывать
SaveChanges, можно выполнить обновление одним SQL-запросом.Это значительно быстрее и снижает нагрузку на приложение, особенно при работе с большими таблицами.
Однако есть важное условие: нужно заранее знать, какое поле вы обновляете и какое значение ему присваиваете, поскольку операция формируется как прямой SQL-update.
Такой подход отлично подходит для массовых операций и оптимизации производительности в EF Core.
24 марта (вторник) в Москве (Лофт Casa Picassa) и онлайн.
В программе три доклада, много кейсов и камерная дискуссия без записи. В фокусе primitive obsession, нагрузка с Load Shedding и Escape Analysis в JIT.
За подробной программой и регистрацией — сюда
Please open Telegram to view this post
VIEW IN TELEGRAM
Vector Search - как это работает (и почему это важно для .NET разработчиков)
Vector search ищет смысловую близость, а не просто точные совпадения.
Он сравнивает данные - текст, изображения или аудио - используя векторные эмбеддинги в многомерном пространстве.
То есть система ищет не одинаковые слова, а похожие по смыслу объекты.
Почему это важно?
Vector search лежит в основе многих AI-функций:
- семантический поиск
- рекомендательные системы
- интеграции с LLM
- умные ассистенты внутри приложений
Добавив векторный поиск в приложение, разработчик может создавать намного более умные продукты, которые понимают смысл запросов пользователя.
Это дает реальную бизнес-ценность - от поиска по документам до персонализированных рекомендаций.
📍 Полный пример реализации
🚀 Max
Vector search ищет смысловую близость, а не просто точные совпадения.
Он сравнивает данные - текст, изображения или аудио - используя векторные эмбеддинги в многомерном пространстве.
То есть система ищет не одинаковые слова, а похожие по смыслу объекты.
Почему это важно?
Vector search лежит в основе многих AI-функций:
- семантический поиск
- рекомендательные системы
- интеграции с LLM
- умные ассистенты внутри приложений
Добавив векторный поиск в приложение, разработчик может создавать намного более умные продукты, которые понимают смысл запросов пользователя.
Это дает реальную бизнес-ценность - от поиска по документам до персонализированных рекомендаций.
📍 Полный пример реализации
🚀 Max
Разработчик показал, как использовать Ollama для извлечения данных из чеков прямо в .NET.
Самая интересная часть оказалась не в том, чтобы отправить изображение модели.
Гораздо сложнее было получить результат, который можно реально использовать в коде.
Обычный текстовый ответ мало помогает, когда нужны структурированные данные:
- позиции из чека
- количество
- цены
- итоговая сумма
Поэтому вместо обычного ответа модель начали просить возвращать JSON.
После этого результат можно сразу маппить в C#-объекты и использовать в приложении.
И именно здесь начинается самое интересное.
Большая часть работы — не код, а правильный prompt.
Если модель:
- округляла цену
- пропускала цифру
- или «придумывала» позицию
приходилось уточнять инструкции.
Это и есть главный сдвиг в таком подходе:
раньше разработчик писал парсеры и regex,
а теперь — настраивает поведение модели через prompt.
Когда модель начинает возвращать структурированные данные,
всё остальное снова превращается в обычный код приложения.
Разбор полной реализации:
https://milanjovanovic.tech/blog/how-to-extract-structured-data-from-images-using-ollama-in-dotnet
🚀 Max
#ai #ollama #dotnet #csharp
Самая интересная часть оказалась не в том, чтобы отправить изображение модели.
Гораздо сложнее было получить результат, который можно реально использовать в коде.
Обычный текстовый ответ мало помогает, когда нужны структурированные данные:
- позиции из чека
- количество
- цены
- итоговая сумма
Поэтому вместо обычного ответа модель начали просить возвращать JSON.
После этого результат можно сразу маппить в C#-объекты и использовать в приложении.
И именно здесь начинается самое интересное.
Большая часть работы — не код, а правильный prompt.
Если модель:
- округляла цену
- пропускала цифру
- или «придумывала» позицию
приходилось уточнять инструкции.
Это и есть главный сдвиг в таком подходе:
раньше разработчик писал парсеры и regex,
а теперь — настраивает поведение модели через prompt.
Когда модель начинает возвращать структурированные данные,
всё остальное снова превращается в обычный код приложения.
Разбор полной реализации:
https://milanjovanovic.tech/blog/how-to-extract-structured-data-from-images-using-ollama-in-dotnet
🚀 Max
#ai #ollama #dotnet #csharp
👩💻 Открытый урок «Как работают структуры данных C# "под капотом"»
🗓 13 апреля в 20:00 МСК
🆓 На этом открытом уроке мы простым и понятным языком разберём структуры данных, что происходит внутри программы, когда она хранит и обрабатывает данные.
Что рассмотрим на вебинаре:
✔ Внутреннее устройство ключевых коллекций
✔ Принципы работы и алгоритмическую сложность операций
✔ Особенности реализации, влияющие на производительность
Кому будет полезно:
✔ Начинающим разработчикам - чтобы углубить понимание платформы .NET и писать эффективный, надёжный код.
✔ Тем, кто готовится к техническим собеседованиям (вопросы о внутреннем устройстве коллекций и сложности операций - классика интервью).
✔ Всем, кто хочет осознанно выбирать структуры данных - избегать типичных ошибок, понимать компромиссы и узкие места.
🔗 Ссылка на регистрацию: https://otus.pw/HE4y/?erid=2W5zFJNNR7F
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
🗓 13 апреля в 20:00 МСК
🆓 На этом открытом уроке мы простым и понятным языком разберём структуры данных, что происходит внутри программы, когда она хранит и обрабатывает данные.
Что рассмотрим на вебинаре:
✔ Внутреннее устройство ключевых коллекций
✔ Принципы работы и алгоритмическую сложность операций
✔ Особенности реализации, влияющие на производительность
Кому будет полезно:
✔ Начинающим разработчикам - чтобы углубить понимание платформы .NET и писать эффективный, надёжный код.
✔ Тем, кто готовится к техническим собеседованиям (вопросы о внутреннем устройстве коллекций и сложности операций - классика интервью).
✔ Всем, кто хочет осознанно выбирать структуры данных - избегать типичных ошибок, понимать компромиссы и узкие места.
🔗 Ссылка на регистрацию: https://otus.pw/HE4y/?erid=2W5zFJNNR7F
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
Что выведет на экран этот код?
Anonymous Quiz
23%
True, One
11%
False, One
16%
True, 0
23%
False, 0
9%
Код не скомпилируется
6%
Возникнет исключение
13%
👩💻 Открытый урок «Производительность кода на примере алгоритмов сортировки»
🗓 21 апреля в 20:00 МСК
🆓 На этом открытом уроке мы разберёмся, как скорость работы программы зависит от выбранного способа решения задачи - на понятном примере сортировки данных
🔍 Что вы узнаете на вебинаре:
• Почему существует несколько способов выполнить одну и ту же задачу
• Как разные подходы к сортировке данных влияют на скорость работы программы
• Почему одни решения подходят для маленьких объёмов данных, а другие — для больших
• Что происходит с программой, когда данных становится в 10, 100 или 1000 раз больше
🎯 Кому будет полезно:
• Тем, кто только присматривается к профессии разработчика на C#
• Новичкам, которые начали изучать C# и хотят понимать, как писать «быстрый» код
• Всем, кто хочет разобраться, от чего зависит скорость работы приложений
🔗 Ссылка на регистрацию: https://otus.pw/GAHM/?erid=2W5zFGdMqeZ
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
🗓 21 апреля в 20:00 МСК
🆓 На этом открытом уроке мы разберёмся, как скорость работы программы зависит от выбранного способа решения задачи - на понятном примере сортировки данных
🔍 Что вы узнаете на вебинаре:
• Почему существует несколько способов выполнить одну и ту же задачу
• Как разные подходы к сортировке данных влияют на скорость работы программы
• Почему одни решения подходят для маленьких объёмов данных, а другие — для больших
• Что происходит с программой, когда данных становится в 10, 100 или 1000 раз больше
🎯 Кому будет полезно:
• Тем, кто только присматривается к профессии разработчика на C#
• Новичкам, которые начали изучать C# и хотят понимать, как писать «быстрый» код
• Всем, кто хочет разобраться, от чего зависит скорость работы приложений
🔗 Ссылка на регистрацию: https://otus.pw/GAHM/?erid=2W5zFGdMqeZ
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
🚀 Динамические Claims в ASP.NET Core
Хотите добавлять права пользователю прямо во время выполнения приложения?
В ASP.NET Core это можно сделать через Claims Transformation.
Что это даёт:
- можно подтягивать данные из базы или внешнего сервиса
- добавлять их в HttpContext.User
- строить гибкие политики авторизации
То есть права пользователя можно расширять динамически, без изменения токена или повторной аутентификации.
Пример из поста:
Во время трансформации claims добавляется новый claim:
CardType = "platinum"
После этого можно создать policy:
- пользователь должен быть аутентифицирован
- у него должен быть claim CardType = platinum
И использовать её в авторизации.
Это особенно полезно для:
- RBAC (role-based access control)
- сложных систем прав
- динамических разрешений из БД
- feature-based доступа
Например:
- premium пользователь
- платиновая карта
- доступ к закрытому API
- платные функции
Claims Transformation позволяет подмешивать эти права на лету.
Полная статья:
https://milanjovanovic.tech/blog/master-claims-transformation-for-flexible-aspnetcore-authorization
Хотите добавлять права пользователю прямо во время выполнения приложения?
В ASP.NET Core это можно сделать через Claims Transformation.
Что это даёт:
- можно подтягивать данные из базы или внешнего сервиса
- добавлять их в HttpContext.User
- строить гибкие политики авторизации
То есть права пользователя можно расширять динамически, без изменения токена или повторной аутентификации.
Пример из поста:
Во время трансформации claims добавляется новый claim:
CardType = "platinum"
После этого можно создать policy:
- пользователь должен быть аутентифицирован
- у него должен быть claim CardType = platinum
И использовать её в авторизации.
Это особенно полезно для:
- RBAC (role-based access control)
- сложных систем прав
- динамических разрешений из БД
- feature-based доступа
Например:
- premium пользователь
- платиновая карта
- доступ к закрытому API
- платные функции
Claims Transformation позволяет подмешивать эти права на лету.
Полная статья:
https://milanjovanovic.tech/blog/master-claims-transformation-for-flexible-aspnetcore-authorization
☁️ Как на самом деле устроены инфраструктурные сервисы Yandex Cloud?
Разработчики Yandex Cloud и Yandex Infrastructure расскажут об этом 16 апреля на встрече для разработчиков, архитекторов и сетевых инженеров.
В программе вас ждут реальные технические варианты реализации и опыт нетривиальных решений разработчиков платформы:
— Инфраструктура как код для управления оповещениями: и никаких проблем
— Развёртывание в ритме танго: как мы заменили оркестрацию процесса установки «хореографией»
— Как мы оптимизируем вывод больших языковых моделей: кэширование, время отклика и ресурсы графических ускорителей
— Как мы строили собственный сервис доставки контента и через что нам пришлось пройти?
— Как мы работаем с уязвимостями на примере современных атак
— Секретный доклад (онлайн трансляция не предусмотрена)
Участники смогут обсудить самые “горячие” вопросы, технические варианты реализации и ошибки с разработчиками сервисов Yandex Cloud и другими участниками.
Когда: 16 апреля офлайн в Москве и онлайн.
Помимо экспертных докладов, офлайн участников ждут развлекательная программа, а тех, кто онлайн: инженерное соревнование в прямом эфире.
Регистрируйтесь, чтобы послушать реальные истории от разработчиков, обменяться опытом и узнать, что скрыто под «капотом» инфраструктурных сервисов, а также какие планы у команды на будущее.
Разработчики Yandex Cloud и Yandex Infrastructure расскажут об этом 16 апреля на встрече для разработчиков, архитекторов и сетевых инженеров.
В программе вас ждут реальные технические варианты реализации и опыт нетривиальных решений разработчиков платформы:
— Инфраструктура как код для управления оповещениями: и никаких проблем
— Развёртывание в ритме танго: как мы заменили оркестрацию процесса установки «хореографией»
— Как мы оптимизируем вывод больших языковых моделей: кэширование, время отклика и ресурсы графических ускорителей
— Как мы строили собственный сервис доставки контента и через что нам пришлось пройти?
— Как мы работаем с уязвимостями на примере современных атак
— Секретный доклад (онлайн трансляция не предусмотрена)
Участники смогут обсудить самые “горячие” вопросы, технические варианты реализации и ошибки с разработчиками сервисов Yandex Cloud и другими участниками.
Когда: 16 апреля офлайн в Москве и онлайн.
Помимо экспертных докладов, офлайн участников ждут развлекательная программа, а тех, кто онлайн: инженерное соревнование в прямом эфире.
Регистрируйтесь, чтобы послушать реальные истории от разработчиков, обменяться опытом и узнать, что скрыто под «капотом» инфраструктурных сервисов, а также какие планы у команды на будущее.
🚀 Почему этот EF Core код тормозит?
Технически - всё ок.
По производительности не очень.
Вот типичная ошибка:
❌ Загружаешь всю сущность (все колонки)
❌ Потом фильтруешь и мапишь уже в памяти
Что происходит:
- лишние данные тянутся из БД
- растёт нагрузка на сеть
- увеличивается потребление памяти
- замедляется приложение
✅ Как правильно:
Используй проекцию через `.Select()` прямо в запросе:
- берёшь только нужные поля
- меньше данных из БД
- быстрее запрос
- меньше нагрузка на систему
📌 Правило простое:
Не тащи всё - бери только то, что используешь
Именно такие мелочи чаще всего дают x2–x10 к скорости.
Технически - всё ок.
По производительности не очень.
Вот типичная ошибка:
❌ Загружаешь всю сущность (все колонки)
❌ Потом фильтруешь и мапишь уже в памяти
Что происходит:
- лишние данные тянутся из БД
- растёт нагрузка на сеть
- увеличивается потребление памяти
- замедляется приложение
✅ Как правильно:
Используй проекцию через `.Select()` прямо в запросе:
- берёшь только нужные поля
- меньше данных из БД
- быстрее запрос
- меньше нагрузка на систему
📌 Правило простое:
Не тащи всё - бери только то, что используешь
Именно такие мелочи чаще всего дают x2–x10 к скорости.