Библиотека шарписта | C#, F#, .NET, ASP.NET
22.2K subscribers
2.68K photos
41 videos
85 files
5K links
Все самое полезное для C#-разработчика в одном канале.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/b60af5a4

Для обратной связи: @proglibrary_feeedback_bot

РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead
Download Telegram
📷 Подборка вакансий для шарпистов

C#/.NET разработчик — 265 000 ₽. Удалёнка

Middle/Senior .NET Developer — от 3 000 до 3 500 $. Удалёнка

C#/.NET-разработчик — Удалёнка

➡️ Еще больше топовых вакансий — в нашем канале C# Jobs

🐸 Библиотека шарписта
Please open Telegram to view this post
VIEW IN TELEGRAM
2
🌼 Бустим рабочее место за 10 шагов

Удаленка — это не только про свободу выбора места работы, но и про умение создать условия для продуктивности.

➡️ В карточках 5 советов по обустройству рабочего места, а остальные в статье.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍1
🧑‍💻 Композиция функций в C#

Функциональное программирование — не просто академическая теория. Это парадигма, превращающая императивный спагетти-код в декларативные, тестируемые и поддерживаемые решения.

Проблема императивного кода:
// Традиционный подход - может быть трудно читать и сложно тестировать
public OrderDto ProcessOrder(int orderId)
{
var order = _dbContext.Orders.Find(orderId);
if (order == null) throw new NotFoundException();

if (order.Status != "Pending")
throw new InvalidOperationException();

var customer = _dbContext.Customers.Find(order.CustomerId);
if (!customer.IsActive)
throw new InvalidOperationException();

var discount = customer.IsVip ? 0.1m :
order.Total > 1000 ? 0.05m : 0m;

order.FinalTotal = order.Total * (1 - discount);
_dbContext.SaveChanges();

return new OrderDto { ... };
}


Проблемы: смешанные обязанности, жёсткая связанность с БД, невозможность переиспользования, хрупкость при изменениях.

Композиционное решение:
public class OrderProcessingPipeline
{
private readonly Func<int, Task<Order?>> _loadOrder;
private readonly Func<Order, Task<Customer?>> _loadCustomer;

public async Task<Result<OrderDto, Error>> ProcessAsync(int orderId)
{
return await Result<Order, Error>
.FromNullable(await _loadOrder(orderId), Error.OrderNotFound)
.BindAsync(_loadCustomer)
.BindAsync(_validateStatus)
.BindAsync(_calculateDiscount)
.BindAsync(_persistOrder)
.MapAsync(_mapToDto);
}
}


Преимущества: единственная ответственность каждой функции, простота тестирования, композируемость, декларативный флоу.

Основа композиции — чистые функции без побочных эффектов:
public static class PricingFunctions
{
public static decimal CalculateDiscount(CustomerType type, decimal total) =>
type switch
{
CustomerType.Vip => total * 0.15m,
CustomerType.Premium => total * 0.10m,
_ => 0m
};

public static decimal ApplyTax(decimal amount, decimal rate) =>
amount * (1 + rate);

// Композиция
public static Func<CustomerType, decimal, decimal> CalculateFinalPrice =>
(type, total) => ApplyTax(total - CalculateDiscount(type, total), 0.08m);
}


Тест такой функции:
[Theory]
[InlineData(CustomerType.Vip, 1000, 918.00)]
public void CalculateFinalPrice_ReturnsExpected(CustomerType type, decimal total, decimal expected)
{
Assert.Equal(expected, PricingFunctions.CalculateFinalPrice(type, total));
}


Начните с малого: замените один цикл на Map, один if/throw на Result. Паттерны быстро станут естественными, и ваша кодовая база скажет спасибо.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
8
📂 File-Scoped Namespaces

Cинтаксис file-scoped namespaces заменяет привычное объявление пространства имён со скобками на однострочное с точкой с запятой.

Раньше каждый файл в C# проекте начинался с namespace MyApp.Services { ... }, что добавляло один уровень отступа ко всему коду внутри. Теперь достаточно написать namespace MyApp.Services; в начале файла, и всё содержимое автоматически находится в этом пространстве имён без дополнительной вложенности.

Код становится более плоским. Вместо того чтобы начинать каждый класс с двух уровней отступа: namespace + class, вы сразу работаете с одним.

При рефакторинге, когда нужно переместить класс в другой namespace, старый подход требовал изменения строки с объявлением. В git diff это выглядело как замена всего файла, даже если логика класса не менялась. C новым подходом меняется только одна строка вверху.

Компилятор не видит разницы — это чисто синтаксический сахар. IL-код идентичен, производительность не меняется.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍8🥱2
🧑‍💻 Когда чистый код всё равно требует комментариев

Чистый код делает программу понятной без лишних объяснений. Но иногда комментарии необходимы, чтобы передать суть решений, которые не уместить в самих строках.

Роберт Мартин в книге «Чистый код» не запрещает комментарии полностью. Он против тех, что дублируют очевидное или устаревают со временем.

Когда комментарии спасают ситуацию

• Сложная логика или хак:
// Обход бага в IE11 с обработкой форм
if (isIE11()) {
polyfillFormSubmit();
}


• Предупреждения о рисках:
// Не трогать: изменение сломает кэш в проде до деплоя новой версии
cache.invalidateOnlyOnRestart();


• Указание на патенты, стандарты или тикеты в баг-трекере.

💬 Когда, по вашему мнению, ещё могут пригодиться комментарии в коде

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔4👍2🔥1
👨‍💻 Span<T> как ref struct

Разбираем вопрос с собеса, в этот раз про Span.

Span<T> даёт доступ к памяти без копий и аллокаций. Но почему его сделали ref struct с кучей запретов, и когда лучше взять Memory<T>?

Ответ лежит в нашем канале с вопросами с собесов

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#dotnet_challenge
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🌚2😢1
Собираем фулл-хаус: 3 курса по цене 1

Хватит выбирать между «полезно», «модно» и «для души». Мы запустили механику, которая позволяет собрать кастомный стек навыков без удара по бюджету: покупаете один курс — два других забираете бесплатно.

В Enterprise ценится фундаментальность. Углубитесь в архитектуру и шаблоны проектирования для построения сложных систем или подтяните алгоритмы и структуры данных для технических интервью.

Для тех, кто хочет понимать математику ML-моделей (вне зависимости от языка) — полный набор по AI: от линала и ML-старта до разработки автономных агентов.

Ну и классика: обновлённый Python как второй язык для скриптов.

Собрать свой пак
2
⚡️ C# убил Builder для простых случаев

Target-typed new убирает дублирование типов при инициализации, а with-выражения делают иммутабельные обновления одной строкой кода.

Раньше при создании объекта приходилось дважды писать тип: var user = new UserDto { ... }. Теперь достаточно User user = new() { ... }. Компилятор выводит тип из контекста — при возврате из методов, в параметрах, элементах коллекций

Работа с иммутабельными объектами традиционно требовала copy-конструкторов, Builder'а или AutoMapper. with-выражение создаёт поверхностную копию с изменением указанных свойств: var updated = user with { Age = 31 }. Компилятор генерирует код копирования автоматически.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱173👾2
📎 Пропущенный аудит

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

Перехватчик SaveChanges ловит изменения из трекера. Но ExecuteUpdate работает напрямую с БД и игнорирует его:
public override async Task<int> SaveChangesAsync(CancellationToken ct = default)
{
// Захватывает изменения от SaveChanges
var entries = ChangeTracker.Entries()
.Where(e => e.State == EntityState.Modified);

// Но не от ExecuteUpdate!
}


Решение через ручной аудит

Добавляйте аудит вручную перед или после ExecuteUpdate:
await context.Products
.Where(p => p.CategoryId == categoryId)
.ExecuteUpdateAsync(s => s
.SetProperty(p => p.Price, newPrice)
.SetProperty(p => p.ModifiedAt, DateTime.UtcNow)
.SetProperty(p => p.ModifiedBy, currentUser));

// Отдельная запись аудита
context.AuditLogs.Add(new AuditLog
{
Action = "BulkPriceUpdate",
EntityType = "Product",
Details = $"Обновил CategoryId={categoryId}, цена={newPrice}"
});
await context.SaveChangesAsync();


Альтернативы для полного аудита

• Триггеры на уровне БД ловят все изменения, включая ExecuteUpdate. Минус — сложнее тестировать и отлаживать.

• Библиотеки расширения EF предлагают BulkUpdate с встроенным аудитом через опции UseAudit.

📍 Навигация: ВакансииЗадачиСобесы

🐸Библиотека шарписта

#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
6😁1
🔄 Дайджест недели

Коротко про события недели.

Microsoft разрабатывает функцию перемещения панели задач в Windows 11

Функция ожидается летом 2026 года как часть усилий по улучшению репутации ОС.

Linux 6.19

Превью .NET 11

Бустим рабочее место за 10 шагов

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
1
🌐 Forward или Reverse Proxy

Forward и Reverse прокси звучат похоже — оба это прокси. Но работают в противоположных направлениях и решают разные задачи. Давайте разберёмся.

➡️ Forward Proxy

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

Forward прокси — это посредник между вами и интернетом.

Главное: целевой сервер не знает, что запрос пришёл от вас. Он видит только прокси.

Зачем его используют:

• Логирование всех запросов
• Блокировка нежелательных сайтов
• Экономия трафика через кеширование
• Контроль безопасности

➡️ Reverse Proxy — противоположная история

Теперь представьте большую компанию с множеством отделов.
Когда кто-то звонит в компанию, он попадает на секретаря. Секретарь решает, в какой отдел перенаправить звонок.
Звонящий не знает внутреннюю структуру компании. Он общается только с секретарём.

Reverse прокси — это секретарь для ваших серверов.

Клиент не знает, что за прокси находятся несколько серверов. Он видит только один публичный адрес.

➡️ Реальные примеры использования

1. Балансировка нагрузки

У вас 5 серверов с одним приложением:
Клиент → Reverse Proxy → [Сервер 1]
→ [Сервер 2]
→ [Сервер 3]
→ [Сервер 4]
→ [Сервер 5]

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

2. Единая точка входа

У вас микросервисная архитектура:

• API на порту 5000
• Frontend на порту 3000
• Админ-панель на порту 8080
• WebSocket сервер на порту 4000

Без reverse прокси пользователю нужно знать все эти порты. А с reverse прокси можно сделать так:
https://mysite.com/api → Сервер на :5000
https://mysite.com/ → Сервер на :3000
https://mysite.com/admin → Сервер на :8080
https://mysite.com/ws → Сервер на :4000


3. SSL/TLS

Вместо настройки HTTPS на каждом сервере, настраиваете один раз на прокси. Проще управлять сертификатами, меньше нагрузка на бэк.

4. Защита и безопасность

Внутренние серверы вообще не видны из интернета. Видна только прокси.

5. Кеширование
Статический контент: картинки, CSS и JS; можно кешировать на прокси. Не нужно каждый раз обращаться на бэк.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍184🙏1
😊 Менторство на работе

Вас просят поменторить нового разработчика. Давайте разберемся, когда это возможность, а когда — скрытая эксплуатация.

Идеальная ситуация

Компания доплачивает 15-20% к зарплате, джун быстро схватывает и задает умные вопросы. Вы получаете деньги, прокачиваете скилл объяснения. Все в плюсе.

Ловушка «помоги новенькому»

«Покажи, как тут все устроено» может быть и без доплаты, без официального оформления. Вы делаете две работы за одну зарплату. Времени уходит 5-10 часов в неделю, дедлайны остаются прежними. Через месяц выгорание и раздражение на джуна, хотя он не виноват.

Выход: либо официальное оформление с корректировкой нагрузки, либо вежливое «сейчас не могу взять это на себя».

Человек не понимает базовых вещей после третьего объяснения, не гуглит, не пытается разобраться сам. Вы превращаетесь в костыль, каждый вопрос съедает время, а прогресса нет. Проблема часто не в способностях, а в мотивации или ошибке на этапе найма.

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

💬 Менторили джунов? Как у вас это происходило? Делитесь в комментах 👇

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#entry_point
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍3
🆚 Микросервисная архитектура vs Монолит

Монолит — это традиционный подход к разработке, где всё приложение представляет собой единое целое. Весь код, бизнес-логика, UI и доступ к данным находятся в одном проекте и деплоятся как единая единица.

Микросервисы — это архитектурный подход, при котором приложение разбивается на набор небольших, независимых сервисов.

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

Каждый сервис — это отдельное .NET приложение (мини-монолит) со своей БД, API и бизнес-логикой.

Ключевые отличия

➡️ Структура и организация кода

Монолит:

• Один репозиторий, один проект
• Общие зависимости для всех модулей
• Прямые вызовы методов между компонентами

Микросервисы:

• Множество репозиториев или монорепозиторий
• Независимые зависимости для каждого сервиса
• Коммуникация через API: HTTP, gRPC, сообщения

➡️ Развертывание

Монолит:

• Деплой всего приложения целиком
• Один сервер или кластер серверов
• Простая инфраструктура

Микросервисы:

• Независимый деплой каждого сервиса
• Контейнеризация и оркестрация
• Сложная инфраструктура с API Gateway, Service Discovery

➡️ Масштабирование

Монолит:

• Вертикальное масштабирование — увеличение ресурсов сервера
• Горизонтальное масштабирование всего приложения
• Неэффективно, если нагрузка только на одну функцию

Микросервисы:

• Горизонтальное масштабирование конкретных сервисов
• Масштабируются только те части, которым нужно
• Оптимизация затрат на инфраструктуру

➡️ База данных

Монолит:

• Одна общая база данных
• ACID транзакции работают нативно
• Простота в управлении данными

Микросервисы:

• База данных на каждый сервис
• Распределенные транзакции
• Eventual Consistency

➡️ Технологический стек

Монолит:

• Единый стек технологий
• Сложно внедрить новые технологии
• Один язык программирования

Микросервисы:

• Каждый сервис может использовать свой стек
• Гибкость в выборе технологий
• Можно смешивать .NET, Node.js, Python и др.

Микросервисы — не серебряная пуля, а инструмент для решения конкретных проблем масштаба и сложности.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🥱5