C# (C Sharp) programming
18.1K subscribers
978 photos
51 videos
8 files
780 links
По всем вопросам- @notxxx1

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

#VRHSZ
Download Telegram
🔥 Полезная подборка каналов только код, практика и самые передовые инструменты, которые используют разработчики прямо сейчас.👇

🖥 C#: t.me/csharp_1001_notes

🖥 ИИ: t.me/ai_machinelearning_big_data

🖥 Python: t.me/pythonl

🖥 Linux: t.me/linuxacademiya

🖥 C++ t.me/cpluspluc

🖥 Docker: t.me/DevopsDocker

🖥 Хакинг: t.me/linuxkalii

🖥 Devops: t.me/DevOPSitsec

👣 Golang: t.me/Golang_google

🖥 Аналитика: t.me/data_analysis_ml

🖥 Javascript: t.me/javascriptv

🖥 Java: t.me/javatg

🖥 Базы данных: t.me/sqlhub

👣 Rust: t.me/rust_code

🤖 Технологии: t.me/vistehno

💰 Экономика и инвестиции в ИИ t.me/financeStable

💼 Актуальные вакансии: t.me/addlist/_zyy_jQ_QUsyM2Vi

🖥 Chatgpt бот в тг: t.me/Chatgpturbobot

📚 Бесплатные ит-книги: https://xn--r1a.website/addlist/HwywK4fErd8wYzQy

🖥Подборка по Golang: https://xn--r1a.website/addlist/MUtJEeJSxeY2YTFi

⚡️ Лучшие ИИ ресурсы: https://xn--r1a.website/addlist/2Ls-snqEeytkMDgy

Самое лучшее в этом: ты учишься даже тогда, когда “нет времени, просто потому что читаешь правильную ленту.
Please open Telegram to view this post
VIEW IN TELEGRAM
Если вы думаете, что EF Core «медленный» в 2026 — вы просто живёте в прошлом.

Одна из мощных современных фич — bulk-операции, в том числе массовое удаление.

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

Пример: e-commerce платформа хочет удалить все корзины, созданные более года назад. Раньше это выглядело так: загрузили сущности в память, RemoveRange, SaveChanges — лишние аллокации, трафик и нагрузка.

Теперь достаточно:


context.Carts
.Where(o => o.CreatedOn < DateTime.Now.AddYears(-1))
.ExecuteDelete();


В результате выполняется один SQL DELETE, который напрямую удаляет старые записи в базе — быстро и без промежуточных объектов в памяти.

⚠️ Важно: bulk-операции обходят change tracker EF. Если в контексте уже есть отслеживаемые сущности, состояние может рассинхронизироваться — учитывайте это в логике.

EF Core давно уже не просто ORM, а полноценный инструмент для высоконагруженных систем. И у него есть ещё много фич, которые серьёзно упрощают жизнь разработчику.

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

В .NET всё настраивается один раз через IHttpClientFactory (и Aspire, если используешь его):

• Service discovery для service-to-service вызовов
• Встроенная устойчивость к сбоям (retry, handling transient errors)

После этого ты просто инжектишь клиент и пишешь бизнес-логику — без ручной магии с сокетами, таймаутами и DNS.

Чистый код. Меньше багов. Больше фокуса на логике.

Полный разбор
Please open Telegram to view this post
VIEW IN TELEGRAM
⚡️ КАК СДЕЛАТЬ ИНТЕГРАЦИИ БЕЗ ПОТЕРИ ДАННЫХ

Вы регистрируете пользователя и сразу вызываете внешние сервисы:

- сохраняете пользователя в БД
- отправляете welcome-email
- публикуете событие в event bus


public async Task RegisterUserAsync(User user, CancellationToken token)
{
_userRepository.Insert(user);

await _unitOfWork.SaveChangesAsync(token);

await _emailService.SendWelcomeEmailAsync(user, token);
await _eventBus.PublishAsync(new UserRegisteredEvent(user.Id), token);
}

Вопрос
Что здесь может пойти не так?

Проблемы

— Пользователь сохранён, но email не отправился
— Email отправился, но событие не опубликовалось
— Событие ушло, а транзакция в БД откатилась
— При ретраях возможны дубли

Чем больше внешних вызовов, тем больше точек отказа и сложнее обработка ошибок.

Решение — Outbox Pattern

Сохраняем пользователя и событие в одной атомарной транзакции:


public async Task RegisterUserAsync(User user, CancellationToken token)
{
_userRepository.Insert(user);

_outbox.Insert(new UserRegisteredEvent(user.Id));

await _unitOfWork.SaveChangesAsync(token);
}


Дальше отдельный процесс:

• читает сообщения из Outbox
• отправляет email
• публикует события
• помечает как обработанные

Результат

— Нет рассинхронизации между БД и внешними сервисами
— Гарантированная доставка событий
— Возможность безопасных повторов
— Простая и надёжная архитектура для распределённых систем
Please open Telegram to view this post
VIEW IN TELEGRAM
Что выведет на экран этот код?
Anonymous Quiz
14%
1,2,3 1,2,3
6%
1,2,3 0,0,0
42%
42,2,3 0,0,0
32%
42,2,3 42,2,3
7%
🥒
⚡️ Guard Clauses в .NET: проверяй ошибки одной строкой

Хочешь упростить код и избавиться от вложенных if? Используй guard clauses — быстрые проверки входных данных в самом начале метода.

Вместо длинной логики и вложенности ты сразу останавливаешь выполнение, если данные некорректны. Код становится чище, короче и легче для чтения.

В новых версиях .NET появилось много встроенных проверок:

.NET 6:
- ArgumentNullException.ThrowIfNull

.NET 7:
- ArgumentException.ThrowIfNullOrEmpty

.NET 8:
- ThrowIfNullOrWhiteSpace
- ThrowIfNullOrEmpty (для коллекций)
- ThrowIfZero
- ThrowIfNegative
- ThrowIfGreaterThan

Когда это особенно полезно:
- в сервисах и API для валидации входных данных
- в доменных моделях — чтобы не допустить невалидного состояния
- в конструкторах и фабриках

Главная мысль:
Guard clauses — это не про «больше проверок»,
а про ранний отказ и чистую архитектуру.


public class Order
{
public Order(string customerName, decimal amount)
{
ArgumentException.ThrowIfNullOrWhiteSpace(customerName);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount);

CustomerName = customerName;
Amount = amount;
}

public string CustomerName { get; }
public decimal Amount { get; }
}
✔️ Unity обещает создание игр через текстовые команды.

CEO Unity Мэтт Бромберг определил "AI-driven authoring" как приоритетное направление развития компании на 2026 год. Компания готовит инструмент, который сможет собирать казуальные игры с нуля, используя только естественный язык.

Бета-версию Unity AI покажут на GDC в марте 2026 года. Технология будет работать нативно внутри платформы, Unity возьмет топовые внешние модели и доучит их понимать контекст движка.

В компании уверены, что такой подход даст результат лучше, чем попытки заставить универсальные LLM писать сложный игровой код. Цель - окончательная демократизация геймдева, порог входа в который, похоже, скоро исчезнет совсем.
wccftech.com
Please open Telegram to view this post
VIEW IN TELEGRAM
Что выведет на экран этот код?
Anonymous Quiz
18%
enum, enum, enum
14%
enum, enum, object
53%
enum, object, object
7%
object, object, object
8%
🥒
⚡️ КАСТОМИЗАЦИЯ ИСТОРИИ МИГРАЦИЙ В ENTITY FRAMEWORK

В Entity Framework можно гибко настроить таблицу истории миграций и это полезно в реальных проектах.

По умолчанию EF создаёт таблицу __EFMigrationsHistory в стандартной схеме (dbo).
Но часто требуется:

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

Это можно сделать прямо при настройке DbContext через метод MigrationsHistoryTable.



services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(
connectionString,
sqlOptions =>
{
sqlOptions.MigrationsHistoryTable(
tableName: "MigrationHistory",
schema: "infrastructure"
);
}));


// Теперь EF будет хранить историю миграций в:
// infrastructure.MigrationHistory
Please open Telegram to view this post
VIEW IN TELEGRAM
⚡️ Async-коммуникация ломает интеграционные тесты.

Вы отправляете событие в одном модуле… и что дальше? Ждать?

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

Решение — не ждать. Опрос (Polling).

Я использую паттерн Retry Assertion для тестирования асинхронного взаимодействия между модулями:

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

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

Пример:


var userId = await Sender.Send(new RegisterUserCommand(...));

var result = await Poller.WaitAsync(
TimeSpan.FromSeconds(15),
async () =>
{
return await Sender.Send(new GetCustomerQuery(userId));
});

result.Should().NotBeNull();
result.IsSuccess.Should().BeTrue();
Динамическая строка подключения в 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();
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.