C# (C Sharp) programming
18.3K subscribers
933 photos
47 videos
8 files
753 links
По всем вопросам- @haarrp

C# - канал Senior C# разработчика.

@ai_machinelearning_big_data - Machine learning

@itchannels_telegram - 🔥лучшие ит-каналы

@csharp_ci - C# академия

@pythonlbooks- книги📚

Реестр РКН: https://clck.ru/3Fk3kb

#VRHSZ
Download Telegram
Динамическая строка подключения в EF Core - когда и зачем это нужно

Факт
В EF Core можно задать connection string динамически при регистрации DbContext. Для этого используется делегат, который выполняется в момент получения контекста из DI.

Как это работает

При регистрации DbContext вы передаёте функцию, которая получает IServiceProvider.
Внутри неё можно:
- получить любой сервис
- определить текущего пользователя или tenant
- выбрать нужную базу данных
- и только после этого установить строку подключения

Пример


builder.Services.AddDbContext<OrdersDbContext>((sp, o) =>
{
var tenantProvider = sp.GetRequiredService<TenantProvider>();
var connectionString = tenantProvider.GetConnectionString();

o.UseSqlServer(connectionString);
});


Dynamic DbContext configuration - ключевой паттерн для масштабируемых multi-tenant систем.
Если приложение растёт и базы начинают разделяться по клиентам или регионам, этот подход становится стандартом архитектуры.
🖥 Асинхронщина ломает интеграционные тесты? Есть простой паттерн

Проблема:

Вы отправляете событие в одном модуле…
И начинаете ждать.

Если используете Thread.Sleep
готовьтесь к:
- медленным тестам
- flaky-падениям
- ненависти от CI/CD

Решение — Retry Assertion (Polling)

Как работает паттерн:

1. Выполняем команду (Module A)
2. Периодически опрашиваем состояние (Module B)
3. Ждём, пока результат появится
4. Делаем assert

Схема:

- Execute Command (Module A)
- Poll Query (Module B) until result != null
- Assert

Почему это лучше:

- Нет случайных падений
- Тесты работают быстрее
- Полностью детерминированное поведение
- Отлично подходит для async, событий и modular monolith

Главная идея:

Не жди фиксированное время — жди состояние.

Такой подход превращает нестабильные интеграционные тесты в надёжные и быстрые.
Please open Telegram to view this post
VIEW IN TELEGRAM
💡 Soft delete в EF Core без лишней логики в сервисах

Удалять данные физически — не всегда хорошая идея.
Логи, аудит, восстановление, аналитика — всё это требует 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
🖥 Разделяйте код с помощью Domain Events

Без событий сервисы быстро превращаются в «комбайн»:
— бизнес-логика
— отправка 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

Пример:


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
- Защиту от перегрузки

Подключается максимально просто:


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
C# 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.
Что выведет на экран этот код?
Anonymous Quiz
30%
1, 1
24%
1, 2
15%
2, 2
18%
2, 1
14%
🥒
Есть более эффективный способ делать массовые обновления в EF Core.

Начиная с EF Core 7 появился метод ExecuteUpdate, который позволяет обновлять записи напрямую на стороне базы данных.

Вместо того чтобы загружать объекты в память, изменять их в цикле и затем вызывать SaveChanges, можно выполнить обновление одним SQL-запросом.

Это значительно быстрее и снижает нагрузку на приложение, особенно при работе с большими таблицами.

Однако есть важное условие: нужно заранее знать, какое поле вы обновляете и какое значение ему присваиваете, поскольку операция формируется как прямой SQL-update.

Такой подход отлично подходит для массовых операций и оптимизации производительности в EF Core.
Vector Search - как это работает (и почему это важно для .NET разработчиков)

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
FluentValidation делает валидацию входных данных очень простой.

Знаете ли вы, что валидаторы поддерживают DI (Dependency Injection)?

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

Посмотрите пример в коде ниже.
🚀 Динамические 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
🚀 Почему этот EF Core код тормозит?

Технически - всё ок.
По производительности не очень.

Вот типичная ошибка:

Загружаешь всю сущность (все колонки)
Потом фильтруешь и мапишь уже в памяти

Что происходит:
- лишние данные тянутся из БД
- растёт нагрузка на сеть
- увеличивается потребление памяти
- замедляется приложение

Как правильно:

Используй проекцию через `.Select()` прямо в запросе:

- берёшь только нужные поля
- меньше данных из БД
- быстрее запрос
- меньше нагрузка на систему

📌 Правило простое:
Не тащи всё - бери только то, что используешь

Именно такие мелочи чаще всего дают x2–x10 к скорости.
Что выведет на экран этот код?
Anonymous Quiz
15%
False False
29%
False True
26%
True True
21%
True False
9%
🥒
⚙️ Как правильно работать с настройками в .NET?

В .NET есть 3 основных интерфейса для конфигурации.
И если выбрать не тот — приложение может просто игнорировать изменения в настройках.

Разбираем просто 👇

1️⃣ IOptions
- читается один раз при запуске
- кэшируется на всё время жизни приложения
- подходит для статических настроек

2️⃣ IOptionsSnapshot
- пересчитывается на каждый запрос
- подхватывает изменения в appsettings.json без перезапуска
- идеален для Web API (Scoped)

3️⃣ IOptionsMonitor
- обновляется в реальном времени
- триггерит событие при изменении настроек
- подходит для фоновых сервисов (Singleton)

Главное правило:

👉 статические настройки → IOptions
👉 веб-приложения → IOptionsSnapshot
👉 фоновые сервисы с реакцией на изменения → IOptionsMonitor

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

А это одна из самых частых и незаметных ошибок в .NET.

Подробнее