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

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

#VRHSZ
Download Telegram
⚡️ Почему Repository поверх EF Core часто превращается в лишний слой

Многие .NET-проекты начинают с отдельных репозиториев:


GetPostsByUser(...)
GetPopularPosts(...)
GetPostsByCategory(...)
GetRecentViralPosts(...)


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

Проблема в том, что DbContext уже предоставляет возможности, близкие к Repository и Unit of Work. Дополнительный слой часто усложняет запросы, скрывает возможности EF Core и создаёт абстракцию поверх абстракции.

Один из вариантов решения — Specification Pattern.

Каждая спецификация описывает отдельное условие выборки:


var specification =
new ViralPostSpecification(minLikesCount: 150);

var posts = await dbContext.Posts
.ApplySpecification(specification)
.Select(post => post.ToDto())
.ToListAsync(cancellationToken);


Спецификации можно переиспользовать и комбинировать:


var combinedSpec =
recentSpec.And(highEngagementSpec);


Что это даёт:

- фильтры не размножаются по репозиториям
- запросы остаются рядом с бизнес-правилами
- спецификации проще тестировать
- условия можно комбинировать
- IQueryable продолжает переводиться в SQL

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

#dotnet #csharp #efcore #architecture
Please open Telegram to view this post
VIEW IN TELEGRAM
Media is too big
VIEW IN TELEGRAM
😨 Opus 5 собрал онлайн-шутер в духе Call of Duty практически с одного промпта

Автор попросил модель сделать FPS на Three.js с AAA-графикой, физикой и детализацией, а работу разбить между несколькими субагентами.

Отдельным агентам поручили:
• реализовывать разные части игры
• проверять результат визуально
• жёстко критиковать качество
• повторять итерации, пока результат не станет максимально близок к Call of Duty

Fвтор выложил репозиторий и исходный промпт, поэтому результат можно проверить самому.

промпт:

I want you to build a first-person shooter at the level of the most recent Call of Duty games. It should be utterly perfect, visually beautiful, with every single thing done at AAA quality—from textures to physics to anything you could think of.

Fan out sub-agents and have sub-agents tackle each one individually so that the game is utterly perfect. You should /loop on each item and have a separate sub-agent check it visually to ensure it looks triple A. That separate sub-agent should be a really harsh critic, and if it doesn't look triple A, it should keep going.

Don't stop until each sub-agent is utterly wowed with the quality when compared with the actual Call of Duty game. It should literally compare them side by side blind and say which one looks better. Do this in ThreeJS. /loop until it's utterly perfect. Fan out sub-agents and ultracode.


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

Геймдев меняется быстрее, чем многие ожидали. 😨
Каждый год в середине лета мы с друзьями собираемся на E-CODE. Ещё относительно молодая конфа Ozon Tech стала уже своего рода легендой. Не удивительно: хардовость официальной части здесь не уступает громкости афтерпати.

На E-CODE 2026 нас по части бэкэнда ждут:

• Runtime Async с его + и –
• трассировка с eBPF
• ускорение на SIMD
• построение кастомной real-time системы видеоаналитики
• сравнительный анализ алгоритмов сборки мусора

Фишка этого года — брейнринг разработчиков и ИИ. Эксперты разберут сложные кейсы и сравнят свои подходы с тем, что предложит машина. Победителя выберет зал.

Регистрируйтесь сейчас, чтобы услышать это всё вживую: https://ecode.ozon.tech/

12 и 13 сентября, Москва. До встречи на E-CODE!
Microsoft выпустила бесплатный курс по модернизации старого .NET

.NET Modernization for Beginners показывает, как перенести legacy ASP.NET-приложение на .NET 10 с помощью GitHub Copilot modernization agent.

Внутри:

- анализ старого кода и зависимостей;
- генерация плана миграции;
- пошаговый upgrade приложения;
- деплой обновлённой версии в Azure App Service.

Интересно, что агент сначала создаёт assessment.md, plan.md и tasks.md, а разработчик может проверить план до изменения кода.

Сам курс бесплатный и open source. Для практики понадобится GitHub Copilot.

https://devblogs.microsoft.com/dotnet/announcing-dotnet-modernization-for-beginners/
Copilot научили разбирать MSBuild-логи прямо в VS Code

Microsoft выпустила MSBuild Binlog Analyzer — расширение, которое превращает сложные .binlog в понятный диалог с GitHub Copilot.

Можно спросить:

почему упала сборка;
какие targets и tasks тормозят;
что изменилось между двумя сборками;
почему сломалась incremental build.

Copilot опирается на реальные данные лога через MCP, умеет находить причину ошибки, предлагать исправление и проверять результат повторной сборкой. Есть сравнение с baseline, поиск регрессий и загрузка логов из GitHub Actions и Azure DevOps.

https://devblogs.microsoft.com/dotnet/msbuild-binlog-analyzer-vscode/
Clean Code: сложный `if` лучше превратить в понятное правило

Такой код приходится расшифровывать каждый раз:


if (invoice is not null &&
invoice.Status == InvoiceStatus.Unpaid &&
invoice.DueDateUtc < DateTime.UtcNow &&
invoice.Balance > 0 &&
!invoice.SilenceWarnings &&
invoice.Customer.Email is not null)
{
SendOverdueWarning(invoice);
}


Условие растёт, бизнес-правило теряется среди технических деталей, а изменение одного требования превращается в рискованный рефакторинг.

Вынесем правило в метод с говорящим именем:


if (invoice.ShouldSendOverdueWarning(nowUtc))
{
SendOverdueWarning(invoice);
}


Само правило:


public bool ShouldSendOverdueWarning(DateTime nowUtc)
{
return Status == InvoiceStatus.Unpaid
&& DueDateUtc < nowUtc
&& Balance > 0
&& !SilenceWarnings
&& Customer.Email is not null;
}


Теперь вызывающий код отвечает на вопрос что происходит, а метод хранит детали когда это разрешено.

### Почему исходный вариант плохо тестируется

Он напрямую использует DateTime.UtcNow. Результат зависит от реального времени, поэтому тест может вести себя по-разному в разные моменты.

После передачи времени параметром тест становится предсказуемым:


[Fact]
public void Sends_warning_for_overdue_unpaid_invoice()
{
var invoice = new Invoice
{
Status = InvoiceStatus.Unpaid,
DueDateUtc = new DateTime(2026, 7, 20),
Balance = 1500,
SilenceWarnings = false,
Customer = new Customer { Email = "[email protected]" }
};

var nowUtc = new DateTime(2026, 7, 28);

Assert.True(invoice.ShouldSendOverdueWarning(nowUtc));
}


Для больших проектов вместо DateTime можно внедрить TimeProvider. Главное правило: время, сеть, файловая система и случайность не должны быть скрыты внутри бизнес-логики.
Please open Telegram to view this post
VIEW IN TELEGRAM
C# 15 наконец избавляет вложенные циклы от флагов и `goto`

break и continue могут указывать метку внешнего цикла. Это позволяет сразу выйти из нескольких уровней вложенности или перейти к следующей итерации нужного цикла.


outer: for (int row = 0; row < grid.Height; row++)
{
for (int column = 0; column < grid.Width; column++)
{
if (grid[row, column].IsBlocked)
{
continue outer;
}

if (grid[row, column].IsGoal)
{
break outer;
}
}
}


Здесь:

- continue outer прекращает внутренний цикл и запускает следующую итерацию внешнего;
- break outer полностью выходит из обоих циклов.

Раньше для такого поведения приходилось использовать булевы флаги, дополнительные проверки или goto.

В C# 15 этот код можно записать короче и понятнее.

Особенно удобно при обходе:

- матриц;
- вложенных коллекций;
- игровых карт;
- таблиц;
- сложных структур данных.

Небольшое изменение, которое убирает много лишнего кода из вложенных циклов.
C#-приём: точное сложение `double` через алгоритм Кэхэна

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

Обычный вариант:


double sum = 0;

foreach (double value in values)
{
sum += value;
}


Более точный вариант:


public static double KahanSum(ReadOnlySpan<double> values)
{
double sum = 0;
double compensation = 0;

foreach (double value in values)
{
double adjusted = value - compensation;
double next = sum + adjusted;

compensation = (next - sum) - adjusted;
sum = next;
}

return sum;
}


Переменная compensation сохраняет часть числа, потерянную при округлении, и возвращает её в следующее сложение.

Использование:


double[] values = { 0.1, 0.2, 0.3, 0.4 };

double result = KahanSum(values);


Метод полезен для:

- научных расчётов;
- статистики и аналитики;
- обработки сигналов;
- симуляций;
- накопления миллионов небольших значений.

ReadOnlySpan<double> позволяет передавать массивы и участки памяти без дополнительных копирований.

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

Для денежных расчётов по-прежнему лучше использовать decimal. А когда нужна скорость double и повышенная численная устойчивость – пригодится Kahan Summation.
## В C# появился safe — но это не аналог Rust

В C# 15 тестируется новый контекстный модификатор safe для кода на границе с нативной памятью.

Главная проблема P/Invoke: компилятор видит сигнатуру метода, но не может проверить, насколько безопасно ведёт себя код внутри подключённой библиотеки. Теперь разработчик должен явно зафиксировать решение:


[LibraryImport("libc")]
internal static safe partial int getpid();

[LibraryImport("libc")]
internal static unsafe partial nuint strlen(byte* value);


safe означает, что вызов не требует `unsafe`-контекста со стороны пользователя API.

unsafe предупреждает: безопасность зависит от условий, которые компилятор проверить не может. Такой метод разрешено вызывать только внутри блока:


unsafe
{
nuint length = strlen(pointer);
}


Та же логика применяется к полям структур с явным расположением памяти:


[StructLayout(LayoutKind.Explicit)]
struct Packet
{
[FieldOffset(0)]
public safe long Id;

[FieldOffset(0)]
public unsafe nint Pointer;
}


Если не указать ни safe, ни unsafe, компилятор выдаст ошибку при включённых новых правилах безопасности. :contentReference[oaicite:0]{index=0}

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

C# постепенно меняет подход к unsafe: риск должен быть обозначен в контракте метода и локализован в конкретных участках программы, а не спрятан внутри большого `unsafe`-класса.

Пока эта модель находится в preview и может измениться до стабильного выпуска C# 15.
Лето, ИТ-Пикник и музыка известных артистов уже через несколько дней!

8 августа в Коломенском пройдет ИТ-Пикник.

В программе — выступления проекта LAB Антона Беляева, IOWA, Cream Soda, Pompeya, мартина и Совы.

А днем — научпоп-лекции, дискуссии об ИИ и больших языковых моделях, мастер-классы и интерактивы. Полезные знакомства и развлечения тоже будут.

Зарегистрироваться и узнать подробности можно на сайте мероприятия.

В билет входит +1 — можно позвать близких и друзей.

До встречи в месте притяжения ИТ.
🔥 Request-response через брокер сообщений в C#: мощный паттерн, который легко превратить в распределённый дедлок

Один сервис отправляет сообщение в RabbitMQ или NATS и ждёт ответ.

Для вызывающего кода это выглядит почти как обычный await, хотя под капотом работают две независимые очереди:


Requester -> request -> Responder
Requester <- response <- Responder


В RabbitMQ запрос обычно содержит ReplyTo и уникальный CorrelationId, чтобы клиент понял, к какому запросу относится ответ.

В NATS для ответа создаётся отдельный inbox subject.

Упрощённая идея на C#:


var requestId = Guid.NewGuid();

await bus.SendAsync(new PriceRequest(
RequestId: requestId,
ProductId: productId
));

var response = await responses
.WaitForAsync<PriceResponse>(
requestId,
timeout: TimeSpan.FromSeconds(2),
cancellationToken);


Плюсы действительно значительные:

- отправителю не нужно знать адрес обработчика;
- responder можно горизонтально масштабировать;
- брокер помогает пережить кратковременный сбой сервиса;
- приложения остаются слабо связанными.

Но это не «HTTP, только через RabbitMQ».

Пока requester ждёт результат, взаимодействие остаётся логически синхронным. Поэтому обязательно нужны:

- таймаут и CancellationToken;
- уникальный CorrelationId;
- обработка поздних и повторных ответов;
- идемпотентность responder;
- ограничение количества одновременных запросов;
- трассировка через обе очереди.

Особенно опасный случай:


Service A ждёт B
Service B ждёт C
Service C ждёт A


Брокер работает, сообщения доставляются, но вся система стоит.

Request-response через messaging полезен, когда нужны location transparency, балансировка обработчиков и единый транспорт между сервисами.

Для простого короткого запроса HTTP или gRPC часто остаются понятнее и быстрее.

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

брокер убирает прямую связь между сервисами, но не убирает зависимость от ответа.
Please open Telegram to view this post
VIEW IN TELEGRAM
💡 C#: заставьте архитектуру ломать CI, если кто-то нарушил правила

Компилятор C# отлично проверяет типы, но ему всё равно, что Application внезапно начал зависеть от Infrastructure.

Code Review тоже не гарантирует, что такое заметят.

Для этого можно использовать Architecture Tests — тесты, которые проверяют не бизнес-логику, а структуру проекта.

Например, через NetArchTest.Rules:


[Fact]
public void Application_Should_Not_Depend_On_Infrastructure()
{
var result = Types
.InAssembly(ApplicationAssembly)
.ShouldNot()
.HaveDependencyOn("MyApp.Infrastructure")
.GetResult();

Assert.True(result.IsSuccessful);
}


Теперь случайный:


Application → Infrastructure


сломает CI так же, как обычный упавший unit-тест.

Можно зафиксировать и naming convention:


[Fact]
public void Handlers_Should_End_With_Handler()
{
var result = Types
.InAssembly(ApplicationAssembly)
.That()
.ImplementInterface(typeof(ICommandHandler<>))
.Should()
.HaveNameEndingWith("Handler")
.GetResult();

Assert.True(result.IsSuccessful);
}


Или заставить сервисы быть sealed:


Types
.InAssembly(ApplicationAssembly)
.That()
.HaveNameEndingWith("Service")
.Should()
.BeSealed();


Особенно полезно это становится в больших Clean Architecture / DDD / Modular Monolith проектах.

Вместо документа:


«Application не должен зависеть от Infrastructure»


получаем исполняемое правило:


нарушил архитектуру → тест упал → PR не прошёл


По сути, архитектура превращается из договорённости команды в часть автоматической проверки проекта.

#CSharp #DotNet #Architecture #Testing
This media is not supported in your browser
VIEW IN TELEGRAM
Запрограммируй робота на Python и поборись за призовой фонд трека 7 500 000 руб на True Tech Champ 2026.

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

Лучшие команды пройдут в финал и будут программировать реальных роботов на офлайн-полигоне 22 октября на большой сцене.
Масштабный финал объединит борьбу за призовой фонд, выступления хедлайнеров, доклады спикеров и активности для всех гостей мероприятия.

Рекомендуемые стеки: Go, Java, Python, C#, C++, JS.

Успей зарегистрироваться и пройти квалификацию до 13 сентября

Реклама. ООО "МВС", ИНН 7707767501, Erid:2VSb5yvnNti
🚀 C# РАЗРАБОТЧИКИ: перестаньте создавать зависимости вручную

Одна из самых частых ошибок в архитектуре .NET - создавать зависимости прямо внутри класса.

Плохой подход:
var companyRepository = new CompanyRepository();
var customerRepository = new CustomerRepository();
var creditService = new CustomerCreditServiceClient();
Проблемы:

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

Лучше использовать Dependency Injection
public class CustomerService(
CompanyRepository companyRepository,
CustomerRepository customerRepository,
CustomerCreditServiceClient creditService)
{
}
Теперь DI-контейнер сам создаёт и передаёт нужные зависимости.

Преимущества:

управление временем жизни (Singleton, Scoped, Transient)
лёгкое использование mock-объектов в тестах
decorators для логирования, кэширования и retry
меньше связности между классами
проще масштабировать и рефакторить проект

Dependency Injection - это не просто возможность ASP.NET Core.

Это один из главных принципов создания чистой, гибкой и поддерживаемой архитектуры C#.

🔥 Ещё 5 приёмов рефакторинга C#, которые стоит знать каждому .NET разработчику:
https://milanjovanovic.tech/blog/5-awesome-csharp-refactoring-tips
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥 .NET 11 Preview 7 вышел, и Microsoft заметно прокачала сразу C#, Runtime, ASP.NET Core и SDK

Один из самых интересных апдейтов в C# — labeled break и continue: теперь можно явно указать, из какого цикла выходить или какой цикл продолжать. Ещё появились union patterns и улучшенная проверка исчерпывающих случаев для закрытых иерархий типов.

В Runtime добавили оптимизации async`/`await, включая runtime async tiering и tail-await. Отдельно продолжают развивать NativeAOT и JIT.

В SDK NativeAOT-версия dotnet CLI теперь включена по умолчанию, как и MSBuild Server. У dotnet test появились общий --timeout, ограничение --maximum-failed-tests и поддержка запуска тестов на устройствах .NET MAUI.

ASP.NET Core получил автоматическую приостановку неактивных Blazor circuits, кеширование SSR через CacheView, новые анализаторы Blazor и встроенную локализацию validation-сообщений.

Ещё из приятного: HTTP request compression, API для DNS-записей, парольная защита ZIP, Complex<T>, улучшения LINQ в EF Core и поддержка Half в SQLite.

.NET 11 Preview 7 вышел 11 августа 2026 года и уже доступен для тестирования.

https://devblogs.microsoft.com/dotnet/dotnet-11-preview-7/
⚙️ Service Discovery в .NET через Consul можно подключить без самописного резолва адресов и ручного перебора инстансов.

Рабочая схема выглядит так:


Install-Package Steeltoe.Discovery.Consul


Регистрируем discovery:


builder.Services
.AddServiceDiscovery(o => o.UseConsul());


В appsettings.json указываем Consul и параметры сервиса:


"Consul": {
"Host": "localhost",
"Port": 8500,
"Discovery": {
"ServiceName": "reporting-service",
"Hostname": "reporting-api",
"Port": 8080
}
}


А дальше самое полезное:


builder.Services
.AddHttpClient<ReportingServiceClient>(client =>
{
client.BaseAddress = new Uri("https://reporting-service");
})
.AddServiceDiscovery()
.AddRoundRobinLoadBalancer();


То есть HttpClient ходит не на конкретный IP:port, а на логическое имя сервиса.

Steeltoe через Consul получает живые инстансы, а RoundRobinLoadBalancer распределяет запросы между ними.

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

По сути, клиентская часть получает простой pipeline:

service name → Consul → healthy instances → load balancing → HTTP request

Для микросервисов на .NET это намного чище, чем городить собственный discovery layer.