Библиотека шарписта | C#, F#, .NET, ASP.NET
Все самое полезное для C#-разработчика в одном канале. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/b60af5a4 Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead
Show more📈 Analytical overview of Telegram channel Библиотека шарписта | C#, F#, .NET, ASP.NET
Channel Библиотека шарписта | C#, F#, .NET, ASP.NET (@csharpproglib) in the Russian language segment is an active participant. Currently, the community unites 21 866 subscribers, ranking 6 212 in the Technologies & Applications category and 30 851 in the Russia region.
📊 Audience metrics and dynamics
Since its creation on невідомо, the project has demonstrated rapid growth, gathering an audience of 21 866 subscribers.
According to the latest data from 10 June, 2026, the channel demonstrates stable activity. Although there has been a change in the number of participants by -87 over the last 30 days and by -4 over the last 24 hours, overall reach remains high.
- Verification status: Not verified
- Engagement rate (ER): The average audience engagement rate is 12.06%. Within the first 24 hours after publication, content typically collects 7.04% reactions from the total number of subscribers.
- Post reach: On average, each post receives 2 638 views. Within the first day, a publication typically gains 1 540 views.
- Reactions and interaction: The audience actively supports content: the average number of reactions per post is 8.
- Thematic interests: Content is focused on key topics such as .net, шарписта, навигация, await, string.
📝 Description and content policy
The author describes the resource as a platform for expressing subjective opinions:
“Все самое полезное для C#-разработчика в одном канале.
По рекламе: @proglib_adv
Учиться у нас: https://proglib.io/w/b60af5a4
Для обратной связи: @proglibrary_feeedback_bot
РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead”
Thanks to the high frequency of updates (latest data received on 11 June, 2026), the channel maintains relevance and a high level of publication reach. Analytics show that the audience actively interacts with content, making it an important point of influence in the Technologies & Applications category.
builder.Services.AddHttpClient<IOrderService, OrderService>(client =>
{
client.BaseAddress = new Uri("https://orders-api/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.Timeout = TimeSpan.FromSeconds(30);
});
public class OrderService(HttpClient client) : IOrderService
{
public async Task<Order?> GetAsync(Guid id, CancellationToken ct)
{
var response = await client.GetAsync($"orders/{id}", ct);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<Order>(ct);
}
}
Сразу добавляйте Polly для повторных попыток и circuit breaker. Без этого первый же временный сбой соседнего сервиса положит весь флоу.
Когда не стоит использовать: если между сервисами тысячи вызовов в секунду с жёсткими требованиями по latency — смотрите в сторону gRPC.
gRPC — когда миллисекунды на счету
gRPC работает поверх HTTP/2, использует бинарную сериализацию через Protocol Buffers и генерирует типизированный клиент из .proto-файла. Это означает меньше трафика, меньше CPU на сериализацию и строгий контракт, нарушение которого не скомпилируется.
// orders.proto
service Orders {
rpc GetOrder (OrderRequest) returns (OrderReply);
}
message OrderRequest { string id = 1; }
message OrderReply { string id = 1; string status = 2; }
// в сервисе
builder.Services.AddGrpcClient<OrdersClient>(o =>
o.Address = new Uri("https://orders-grpc-service"));
public class OrderHandler(OrdersClient grpc)
{
public async Task<OrderReply> Handle(GetOrderQuery q, CancellationToken ct)
=> await grpc.GetOrderAsync(
new OrderRequest { Id = q.Id.ToString() },
cancellationToken: ct);
}
Подводный камень: не тащите gRPC туда, где достаточно REST. Если у вас 10 запросов в минуту — вы просто добавите сложность без выигрыша в производительности.
Публичный API или фронтенд — REST без вариантов. Internal-сервисы с высокой нагрузкой и строгим контрактом — gRPC. Если сомневаетесь — начните с REST, профилируйте, и переходите на gRPC там, где это реально болит.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#il_люминаторКлиент → 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 → Сервер на :40003. SSL/TLS Вместо настройки HTTPS на каждом сервере, настраиваете один раз на прокси. Проще управлять сертификатами, меньше нагрузка на бэк. 4. Защита и безопасность Внутренние серверы вообще не видны из интернета. Видна только прокси. 5. Кеширование Статический контент: картинки, CSS и JS; можно кешировать на прокси. Не нужно каждый раз обращаться на бэк. 📍 Навигация: Вакансии • Задачи • Собесы 🐸 Библиотека шарписта #sharp_view
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_viewvar user = new UserDto { ... }. Теперь достаточно User user = new() { ... }. Компилятор выводит тип из контекста — при возврате из методов, в параметрах, элементах коллекций
Работа с иммутабельными объектами традиционно требовала copy-конструкторов, Builder'а или AutoMapper. with-выражение создаёт поверхностную копию с изменением указанных свойств: var updated = user with { Age = 31 }. Компилятор генерирует код копирования автоматически.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_viewSpan<T> даёт доступ к памяти без копий и аллокаций. Но почему его сделали ref struct с кучей запретов, и когда лучше взять Memory<T>?
Ответ лежит в нашем канале с вопросами с собесов
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#dotnet_challenge// Обход бага в IE11 с обработкой форм
if (isIE11()) {
polyfillFormSubmit();
}
• Предупреждения о рисках:
// Не трогать: изменение сломает кэш в проде до деплоя новой версии cache.invalidateOnlyOnRestart();• Указание на патенты, стандарты или тикеты в баг-трекере. 💬 Когда, по вашему мнению, ещё могут пригодиться комментарии в коде 📍 Навигация: Вакансии • Задачи • Собесы 🐸 Библиотека шарписта #il_люминатор
MyApp.Services { ... }, что добавляло один уровень отступа ко всему коду внутри. Теперь достаточно написать namespace MyApp.Services; в начале файла, и всё содержимое автоматически находится в этом пространстве имён без дополнительной вложенности.
Код становится более плоским. Вместо того чтобы начинать каждый класс с двух уровней отступа: namespace + class, вы сразу работаете с одним.
При рефакторинге, когда нужно переместить класс в другой namespace, старый подход требовал изменения строки с объявлением. В git diff это выглядело как замена всего файла, даже если логика класса не менялась. C новым подходом меняется только одна строка вверху.
Компилятор не видит разницы — это чисто синтаксический сахар. IL-код идентичен, производительность не меняется.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view// Традиционный подход - может быть трудно читать и сложно тестировать
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_люминаторMyApp.Services { ... }, что добавляло один уровень отступа ко всему коду внутри. Теперь достаточно написать namespace MyApp.Services; в начале файла, и всё содержимое автоматически находится в этом пространстве имён без дополнительной вложенности.
Код становится более плоским. Вместо того чтобы начинать каждый класс с двух уровней отступа: namespace + class, вы сразу работаете с одним.
При рефакторинге, когда нужно переместить класс в другой namespace, старый подход требовал изменения строки с объявлением. В git diff это выглядело как замена всего файла, даже если логика класса не менялась. C новым подходом меняется только одна строка вверху.
Компилятор не видит разницы — это чисто синтаксический сахар. IL-код идентичен, производительность не меняется.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_viewIQueryable по всему приложению, считая это эффективным подходом. Но такая практика создаёт серьёзные проблемы с архитектурой и сопровождением кода.
IQueryable позволяет строить динамические SQL-запросы, выполняя фильтрацию на стороне БД, а не в памяти приложения:
var startDate = new DateTime(2023, 1, 1);
// Плохо: загружаем всё в память, затем фильтруем
var allRecords = context.Purchases.ToList();
var filtered = allRecords.Where(p => p.Date > startDate).ToList();
// Хорошо: фильтрация выполняется базой данных
var filtered = context.Purchases
.Where(p => p.Date > startDate)
.ToList();
Второй подход быстрее и эффективнее. Но когда IQueryable начинает путешествовать через слои приложения, возникают проблемы.
Антипаттерн: распространение IQueryable.
Типичный сценарий — IQueryable передаётся через слои:
// Репозиторий
public class PurchaseRepository
{
public IQueryable<Purchase> GetAll() =>
_context.Purchases.AsQueryable();
}
// Сервис бизнес-логики
public class PurchaseService
{
public IQueryable<Purchase> GetActive() =>
_repository.GetAll().Where(p => p.Status == "Active");
}
// Контроллер
var userPurchases = _service.GetActive()
.Where(p => p.UserId == currentUserId);
// Представление
@foreach(var item in Model.Where(p => p.IsCompleted))
{
<!-- рендеринг -->
}
Проблемы:
• Логика доступа к данным размазана по всему приложению
• Нарушен принцип единственной ответственности
• IQueryable наследует IEnumerable — код может не знать, что работает с запросом к БД
• Исключения в runtime, если EF не может преобразовать код в SQL
• Сложное тестирование и отладка
Решить эти проблемы можно, к примеру, паттерном спецификация, или, если спецификации избыточны, передавайте выражения:
public interface IPurchaseRepository
{
Task<List<Purchase>> FindAsync(Expression<Func<Purchase, bool>> predicate);
}
// Использование
var purchases = await _repository.FindAsync(
p => p.IsCompleted && p.UserId == currentUserId);
Запрос по-прежнему выполняется в БД, но логика доступа к данным инкапсулирована.
💬 Как бы вы решили эту проблему
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_viewdotnet run теперь интерактивно выбирает целевой фреймворк и устройство
• dotnet test поддерживает позиционные аргументы
• dotnet watch — горячая перезагрузка для изменений в ссылках и настраиваемые порты
➡️ C# и F#
• C#: передача коллекций как аргументов
• C# расширенная поддержка раскладки памяти
• F#: параллельная компиляция включена по умолчанию, ускорение компиляции вычислительных выражений, новые флаги --disableLanguageFeature и --typecheck-only для FSI.
➡️ Блог разработчиков
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
Available now! Telegram Research 2025 — the year's key insights 
