📰 Дайджест недели
Один пост — лучшее недели.
— TDD в больших проектах
— GitHub Copilot Testing в Visual Studio 2026
— Как выбрать render mode в Blazor
— PVS-Studio 7.41
— Один интерфейс — любой канал для уведомлений
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
Один пост — лучшее недели.
— TDD в больших проектах
— GitHub Copilot Testing в Visual Studio 2026
— Как выбрать render mode в Blazor
— PVS-Studio 7.41
— Один интерфейс — любой канал для уведомлений
📍 Навигация: Вакансии • Задачи • Собесы
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
Энтерпрайз убивает драйв? Стань техническим кофаундером в EdTech-стартапе 🚀
Proglib App — платформа для обучения разработчиков (курсы, квизы, ИИ-агенты). MVP уже в проде, пользователи растут. Проекту нужен сильный инженер, готовый взять на себя роль технического лидера и строить продукт вместе с основателем.
Это вызов для тех, кто перерос роль «просто разработчика» и хочет влиять на архитектуру и бизнес-логику напрямую.
🛠️ Стек (современный Fullstack):
TypeScript, React 18, Express 5, PostgreSQL, Drizzle ORM.
Почему это может быть интересно:
• Никакой бюрократии: только код, продукт и архитектурные решения.
• Инструменты: работа с ИИ-ассистентами (Claude Code, Cursor).
• Рост: путь от MVP до масштабного сервиса в роли партнёра.
Ожидания:
• Крепкий бэкграунд в разработке и понимание архитектуры БД.
• Готовность работать с TS/React/Node.js.
• Автономность и продуктовое мышление.
Удалёнка, гибкий график, полная свобода в реализации идей.
Готов сменить привычный стек на роль кофаундера? Пиши о себе и кидай GitHub 👇
@proglibrary_feedback_bot
Proglib App — платформа для обучения разработчиков (курсы, квизы, ИИ-агенты). MVP уже в проде, пользователи растут. Проекту нужен сильный инженер, готовый взять на себя роль технического лидера и строить продукт вместе с основателем.
Это вызов для тех, кто перерос роль «просто разработчика» и хочет влиять на архитектуру и бизнес-логику напрямую.
🛠️ Стек (современный Fullstack):
TypeScript, React 18, Express 5, PostgreSQL, Drizzle ORM.
Почему это может быть интересно:
• Никакой бюрократии: только код, продукт и архитектурные решения.
• Инструменты: работа с ИИ-ассистентами (Claude Code, Cursor).
• Рост: путь от MVP до масштабного сервиса в роли партнёра.
Ожидания:
• Крепкий бэкграунд в разработке и понимание архитектуры БД.
• Готовность работать с TS/React/Node.js.
• Автономность и продуктовое мышление.
Удалёнка, гибкий график, полная свобода в реализации идей.
Готов сменить привычный стек на роль кофаундера? Пиши о себе и кидай GitHub 👇
@proglibrary_feedback_bot
❤2👍1
📝 Логирование со строковой интерполяцией
Логирование c интерполяцией строк один из самых распространённых источников лишних аллокаций в .NET-приложениях.
Особенность в том, что код выглядит абсолютно нормально и работает правильно — просто дорого.
Что происходит с интерполяцией:
Компилятор разворачивает это примерно в:
Строка формируется до вызова метода. Если уровень логирования
Структурное логирование решает проблему:
Здесь строка — это шаблон. Аргументы передаются отдельно. Логгер сначала проверяет, активен ли уровень, и только потом форматирует сообщение. Если уровень выключен — аллокации нет вообще.
Бонус: структурное логирование позволяет индексировать поля в системах вроде Seq, Elasticsearch, Datadog — вы сможете искать по UserId как по полю, а не парсить текст.
Начиная с .NET 6 есть ещё лучший вариант через compile-time source generators:
Никаких аллокаций, никакого боксинга, максимальная производительность — и всё это без изменения читаемости кода.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#il_люминатор
Логирование c интерполяцией строк один из самых распространённых источников лишних аллокаций в .NET-приложениях.
Особенность в том, что код выглядит абсолютно нормально и работает правильно — просто дорого.
Что происходит с интерполяцией:
logger.LogInformation($"User {userId} logged in at {time}");Компилятор разворачивает это примерно в:
logger.LogInformation(string.Format("User {0} logged in at {1}", userId, time));Строка формируется до вызова метода. Если уровень логирования
Information отключён, строка всё равно создаётся, занимает память и тут же выбрасывается сборщиком мусора.Структурное логирование решает проблему:
logger.LogInformation("User {UserId} logged in at {Time}", userId, time);Здесь строка — это шаблон. Аргументы передаются отдельно. Логгер сначала проверяет, активен ли уровень, и только потом форматирует сообщение. Если уровень выключен — аллокации нет вообще.
Бонус: структурное логирование позволяет индексировать поля в системах вроде Seq, Elasticsearch, Datadog — вы сможете искать по UserId как по полю, а не парсить текст.
Начиная с .NET 6 есть ещё лучший вариант через compile-time source generators:
// Генерирует оптимальный код на этапе компиляции:
[LoggerMessage(Level = LogLevel.Information, Message = "User {UserId} logged in at {Time}")]
partial void LogUserLogin(int userId, DateTime time);
Никаких аллокаций, никакого боксинга, максимальная производительность — и всё это без изменения читаемости кода.
📍 Навигация: Вакансии • Задачи • Собесы
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21❤6🤔1
🔄 Cake 6.0.0 вышел
Cake — это инструмент автоматизации сборки для .NET-проектов. Скрипты пишутся на C# с простым DSL, который покрывает компиляцию, тестирование, публикацию и всё, что обычно делают в CI/CD пайплайне.
Логирование теперь принимает интерполированные строки напрямую. Сообщение формируется только если текущий уровень логирования активен:
Cake.Sdk получил поддержку in-process NuGet клиента. Теперь через
Добавлена поддержка
Из других изменений: NuGetPack теперь поддерживает поле ReadMe для readme пакета, в AssemblyInfo creator можно добавлять несколько экземпляров одного атрибута, in-process NuGet restore заработал с аутентификацией через приватные фиды.
Есть breaking change для GitLab CI — поля CI_PIPELINE_ID и CI_BUILD_ID сменили тип с int на long, так как на gitlab.com идентификаторы давно вышли за пределы 32 бит.
➡️ Release Notes
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
Cake — это инструмент автоматизации сборки для .NET-проектов. Скрипты пишутся на C# с простым DSL, который покрывает компиляцию, тестирование, публикацию и всё, что обычно делают в CI/CD пайплайне.
Логирование теперь принимает интерполированные строки напрямую. Сообщение формируется только если текущий уровень логирования активен:
Information($"Building {project} v{version}");
Verbose(log => log($"Processing {items.Count} items in {stopwatch.ElapsedMilliseconds} ms"));Cake.Sdk получил поддержку in-process NuGet клиента. Теперь через
#tool и InstallTool можно устанавливать любые NuGet пакеты прямо в инструменты.Добавлена поддержка
.slnx файлов. DotNetTest и парсинг решений теперь распознают XML-формат solution файлов, а автоопределение типа пути включает .slnx.Из других изменений: NuGetPack теперь поддерживает поле ReadMe для readme пакета, в AssemblyInfo creator можно добавлять несколько экземпляров одного атрибута, in-process NuGet restore заработал с аутентификацией через приватные фиды.
Есть breaking change для GitLab CI — поля CI_PIPELINE_ID и CI_BUILD_ID сменили тип с int на long, так как на gitlab.com идентификаторы давно вышли за пределы 32 бит.
📍 Навигация: Вакансии • Задачи • Собесы
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6❤🔥2❤2
💻 Утечки памяти в .NET
В .NET утечки памяти почти никогда не связаны с ручным управлением указателями. Они появляются из-за ошибок в управлении временем жизни объектов.
Главный инструмент .NET для освобождения ресурсов — интерфейс
Без using объект останется жить до следующего прохода GC, а ресурс под ним — ещё дольше:
Для асинхронных ресурсов — await using:
Что проверить в коде
Потоки. Каждый
Крупные аллокации. Массивы, буферы, большие строки — если они создаются в цикле, это быстро давит на GC. Здесь помогает
Object Pooling. Если объекты дорогие в создании и используются часто —
Простое правило: если тип реализует IDisposable, оборачиваем его в using. Остальное — профилировщик покажет.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#il_люминатор
В .NET утечки памяти почти никогда не связаны с ручным управлением указателями. Они появляются из-за ошибок в управлении временем жизни объектов.
Главный инструмент .NET для освобождения ресурсов — интерфейс
IDisposable и его асинхронный вариант IAsyncDisposable. Если объект реализует один из них, значит он держит что-то важное: файловый дескриптор, сетевое соединение, неуправляемую память. Без using объект останется жить до следующего прохода GC, а ресурс под ним — ещё дольше:
// Утечка: поток не закроется при исключении
var stream = new FileStream(path, FileMode.Open);
// Правильно: using гарантирует закрытие даже при исключении
using var stream = new FileStream(path, FileMode.Open);
Для асинхронных ресурсов — await using:
await using var resource = GetAsyncDisposable();
Что проверить в коде
Потоки. Каждый
Stream, StreamReader, StreamWriter должен быть обёрнут в using. Без этого файловые дескрипторы накапливаются.HttpClient. Один из самых частых источников проблем. Его нельзя создавать на каждый запрос, так как это исчерпывает пул сокетов.Крупные аллокации. Массивы, буферы, большие строки — если они создаются в цикле, это быстро давит на GC. Здесь помогает
ArrayPool<T> или System.Buffers.var pool = ArrayPool<byte>.Shared;
byte[] buffer = pool.Rent(4096);
try
{
// работа с буфером
}
finally
{
pool.Return(buffer);
}
Object Pooling. Если объекты дорогие в создании и используются часто —
ObjectPool<T> из Microsoft.Extensions.ObjectPool снижает нагрузку на аллокатор.Простое правило: если тип реализует IDisposable, оборачиваем его в using. Остальное — профилировщик покажет.
📍 Навигация: Вакансии • Задачи • Собесы
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍2
HttpClient.CurlDelegatingHandler — это обработчик в пайплайне HttpClient, который перехватывает запрос и возвращает готовую curl-команду в заголовке ответа. Никакой дополнительной логики писать не нужно.
Поддерживаются GET, POST, PUT и DELETE.
Как подключить:
dotnet add package HttpClient.CurlDelegatingHandler --version 1.0.0-alpha.1
Как использовать
Передаёте
CurlDelegatingHandler при создании клиента. Чтобы запрос не ушёл в сеть, добавляете заголовок CanSend: False. Curl-команда придёт обратно в заголовке outputCurl.using System.Text;
using CurlGenerator;
string url = "https://jsonplaceholder.typicode.com/posts";
string jsonPayload = @"{""title"": ""New Post"", ""body"": ""This is the body"", ""userId"": 1}";
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
var httpClient = new HttpClient(new CurlDelegatingHandler());
httpClient.DefaultRequestHeaders.Add(Settings.CanSend, "False");
var result = await httpClient.PostAsync(url, content);
string outputCurl = result.Headers.GetValues(Settings.OutputCurl).FirstOrDefault();
Console.WriteLine(outputCurl);
Вывод:
curl -X POST 'https://jsonplaceholder.typicode.com/posts' -H 'Content-Type: application/json' ...
DelegatingHandler встраивается в цепочку обработчиков HttpClient. Вместо того чтобы отправлять запрос по сети, он его перехватывает, строит из него curl-строку и возвращает её в заголовке ответа. Вся логика изолирована в одном месте.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤2🌚1
🤔 Спам тимлидам, фейковые офферы и приукрашенное резюме
IT-рынок 2026 года — это не конкурс честных и талантливых. Компании говорят об экологичной культуре, а сами гостят после четырёх этапов отбора.
На одну вакансию — тысяча откликов за сутки. В таких условиях выигрывают не самые опытные, а самые адаптивные.
➡️ Узнать приёмы тех, кто смог
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view
IT-рынок 2026 года — это не конкурс честных и талантливых. Компании говорят об экологичной культуре, а сами гостят после четырёх этапов отбора.
На одну вакансию — тысяча откликов за сутки. В таких условиях выигрывают не самые опытные, а самые адаптивные.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
😢4
Хороший код легко использовать правильно — и сложно использовать неправильно. Это важно при проектировании API: даже внутреннего.
На ревью кода интерфейсов проверяют логику, покрытие тестами, производительность. Но неудобный интерфейс это технический долг, который будет копиться каждый раз, когда кто-то будет использовать этот код.
Вот на что стоит обращать внимание:
• Понятен ли смысл метода. Название должно говорить само за себя без комментария над функцией и без погружения в реализацию.
Если приходится читать тело метода, чтобы понять что он делает, значит имя плохое.
• Предсказуемо ли поведение. Метод с одинаковыми параметрами должен возвращать одинаковый результат.
Скрытые побочные эффекты и неочевидные состояния — прямой путь к багам, которые воспроизводятся через раз.
• Булевые флаги это тревожный знак. Два булевых параметра подряд — почти всегда признак того, что функция делает слишком много или интерфейс не доработан:
ProcessData(bool flag1, bool flag2);
Явные типы — намерение читается сразу:
ProcessData(ProcessMode mode, ValidationOptions options);
• Исключения или типы результата. Исключения созданы для исключительных ситуаций, а типы результата для ожидаемых ошибок.
Смешивать их = заставлять пользователя угадывать, что пойдёт не так и в каком виде.
• Минимум обязательных параметров. Чем больше параметров нужно передать для базового вызова, тем выше порог входа.
Если для простого случая нужно заполнить пять аргументов, скорее всего интерфейс нуждается в умных значениях по умолчанию или отдельном упрощённом методе.
Удобный интерфейс это уважение к тем, кто будет с ним работать. И к себе через полгода, когда придётся вернуться к этому коду.
📍 Навигация: Вакансии • Задачи • Собесы
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13👍5🥱1🌚1
🗓 Merge conflict, но для календаря
В git есть автоматический резолв конфликтов. Теперь такое есть и для встреч — Microsoft Copilot в Outlook и Teams умеет сам переносить встречи, если в расписании возник конфликт.
Работает для личных встреч и 1:1. Групповые звонки, встречи длиннее 5 часов и повторяющиеся реже раза в месяц — пока не поддерживаются. Но для большинства рабочих 1:1 — уже удобно.
➡️ Источник
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
В git есть автоматический резолв конфликтов. Теперь такое есть и для встреч — Microsoft Copilot в Outlook и Teams умеет сам переносить встречи, если в расписании возник конфликт.
Работает для личных встреч и 1:1. Групповые звонки, встречи длиннее 5 часов и повторяющиеся реже раза в месяц — пока не поддерживаются. Но для большинства рабочих 1:1 — уже удобно.
📍 Навигация: Вакансии • Задачи • Собесы
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔6❤3😁2
🛠 Жизни зависимостей в ASP.NET
Одна из самых частых причин непредсказуемого поведения приложений на ASP.NET Core — неправильно выбранное время жизни сервиса. Проблема тихая: приложение запускается, тесты проходят, а в продакшне начинается что-то странное.
Три режима, которые нужно знать
В ASP.NET у каждого сервиса есть время жизни экземпляра.
Transient. Новый экземпляр создаётся каждый раз, когда сервис запрашивается из контейнера. Подходит для лёгких, stateless-сервисов без общего состояния.
Scoped. Один экземпляр на HTTP-запрос. Все зависимости внутри одного запроса получают один и тот же объект. Это стандартный выбор для большинства сервисов в веб-приложениях.
Singleton. Один экземпляр на всё время жизни приложения. Создаётся один раз и переиспользуется во всех запросах.
Где ломается
Самая распространённая ошибка — инжектировать Scoped-сервис в синглтон. Выглядит невинно, но это называется captive dependency, и ASP.NET даже выбросит исключение при старте, если включена валидация.
Синглтон захватывает Scoped-сервис при первом создании и держит его до конца жизни приложения. Scoped-сервис при этом теряет свой контекст, например DbContext, и начинает работать непредсказуемо.
Если нужен Scoped-сервис внутри синглтона, используйте
И включите валидацию зависимостей в dev-окружении — ASP.NET поймает большинство проблем ещё при запуске.
Если не уверены в выборе, начинайте со Scoped. Для веб-приложений это безопасный дефолт. Singleton используйте только тогда, когда явно нужно общее состояние, и убедитесь, что сервис действительно thread-safe. Transient подходит для простых утилит без состояния.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view
Одна из самых частых причин непредсказуемого поведения приложений на ASP.NET Core — неправильно выбранное время жизни сервиса. Проблема тихая: приложение запускается, тесты проходят, а в продакшне начинается что-то странное.
Три режима, которые нужно знать
В ASP.NET у каждого сервиса есть время жизни экземпляра.
Transient. Новый экземпляр создаётся каждый раз, когда сервис запрашивается из контейнера. Подходит для лёгких, stateless-сервисов без общего состояния.
Scoped. Один экземпляр на HTTP-запрос. Все зависимости внутри одного запроса получают один и тот же объект. Это стандартный выбор для большинства сервисов в веб-приложениях.
Singleton. Один экземпляр на всё время жизни приложения. Создаётся один раз и переиспользуется во всех запросах.
Где ломается
Самая распространённая ошибка — инжектировать Scoped-сервис в синглтон. Выглядит невинно, но это называется captive dependency, и ASP.NET даже выбросит исключение при старте, если включена валидация.
// Так делать не надо
public class MySingleton
{
private readonly IScopedService _scoped;
public MySingleton(IScopedService scoped) // Проблема здесь
{
_scoped = scoped;
}
}
Синглтон захватывает Scoped-сервис при первом создании и держит его до конца жизни приложения. Scoped-сервис при этом теряет свой контекст, например DbContext, и начинает работать непредсказуемо.
Если нужен Scoped-сервис внутри синглтона, используйте
IServiceScopeFactory:public class MySingleton
{
private readonly IServiceScopeFactory _scopeFactory;
public MySingleton(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
public void DoWork()
{
using var scope = _scopeFactory.CreateScope();
var scoped = scope.ServiceProvider.GetRequiredService<IScopedService>();
scoped.Execute();
}
}
И включите валидацию зависимостей в dev-окружении — ASP.NET поймает большинство проблем ещё при запуске.
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = true;
options.ValidateOnBuild = true;
});
Если не уверены в выборе, начинайте со Scoped. Для веб-приложений это безопасный дефолт. Singleton используйте только тогда, когда явно нужно общее состояние, и убедитесь, что сервис действительно thread-safe. Transient подходит для простых утилит без состояния.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍1👾1
C# backend-разработчик в команду Security — Офис/гибрид в Москве
Backend Engineer — от 175 000 до 325 000 ₽, удалёнка
C#/.NET разработчик — от 350 000 ₽, удалёнка
Бустер — Удалённо в любом городе мира.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
Всё начинается разумно. В нескольких сервисах повторяется одна логика — контекст БД, аудит, логирование. Выносишь в базовый класс. Код короче, дублирования нет.
Проходит полгода. Простой юнит-тест поднимает глобальную шину событий и запускает рефлексию. Никто не просил. Просто создали экземпляр репозитория.
Что происходит в конструкторе:
public abstract class RepositoryBase<TEntity>
{
protected RepositoryBase(DbContext context)
{
Context = context;
ApplyEntityConfiguration(); // рефлексия
SubscribeToAuditEvents(); // подписка на события
}
}
Производный класс просто вызвал
base() — и получил в нагрузку поведение, которое не запрашивал. Это невидимая связанность: поведение определяется не сигнатурой, а тем, что спрятано в цепочке наследования.Особый случай — глобальное состояние
Статический инициализатор внутри базового класса меняет сериализацию для всего приложения при первой загрузке любого наследника. Не локально — глобально. Молча.
Наследование vs композиция
Глубокое дерево:
RepositoryBase → MongoBase → AuditableBase → ProductRepository. Четыре уровня. Поведение на каждом. Ничего не видно на месте вызова.Декоратор решает то же самое явно:
public class AuditingRepository : IProductRepository
{
private readonly IProductRepository _inner;
private readonly IAuditService _audit;
public async Task<Product> GetByIdAsync(Guid id)
{
await _audit.LogAccessAsync(id);
return await _inner.GetByIdAsync(id);
}
}
Каждый слой делает одну вещь. Зависимости видны. Убрать логирование можно не трогая репозиторий.
Глобальная конфигурация — в точку запуска
Если что-то влияет на всё приложение, оно должно жить в
Program.cs, а не в базовом классе, который загружается неизвестно когда.Базовый класс становится проблемой, когда берёт на себя слишком много. В этот момент он перестаёт быть абстракцией и превращается в объект бога.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5💯3🔥2
Разработчик запустил nugetz.dev — альтернативный интерфейс для поиска по реестру NuGet.
Стандартный поиск на nuget.org работает, но медленно и неудобно. Когда выбираешь библиотеку, хочешь быстро понять: пакет живой, как давно обновлялся, насколько популярен. Сейчас это занимает лишнее время.
Nugetz решает именно это. Тот же реестр, но с чистым интерфейсом, быстрой выдачей и без лишнего шума на экране. Без регистрации и рекламы.
Идея пришла от npmx — похожего инструмента для npm-экосистемы. Rodrigo просто спросил себя, почему у .NET такого нет, и сделал.
📍 Навигация: Вакансии • Задачи • Собесы
#пульс_индустрии
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13👍1
This media is not supported in your browser
VIEW IN TELEGRAM
😢2❤1
📅 Старт курса — 20 апреля.
Если хотите разобраться, как строить управляемые агентные системы:
P.S.
Please open Telegram to view this post
VIEW IN TELEGRAM
📝 Вытащить текст из PDF на C#
Иногда нужно просто достать текст из PDF — для скрипта, личного инструмента или быстрой автоматизации под себя. Руками копировать лень, особенно если файлов несколько. Вот способ сделать это на C# без лишних телодвижений.
Что использовать
Библиотека Free Spire.PDF для .NET. Устанавливается через NuGet одной командой и не требует ничего стороннего — ни ридеров, ни внешних утилит.
Ограничение бесплатной версии — до 10 страниц на документ. Для личных задач или небольших проектов этого хватает.
Базовый пример — одна страница:
Весь документ сразу:
Что ещё умеет библиотека
Зашифрованные PDF открываются передачей пароля прямо в
Для таблиц есть отдельный
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view
Иногда нужно просто достать текст из PDF — для скрипта, личного инструмента или быстрой автоматизации под себя. Руками копировать лень, особенно если файлов несколько. Вот способ сделать это на C# без лишних телодвижений.
Что использовать
Библиотека Free Spire.PDF для .NET. Устанавливается через NuGet одной командой и не требует ничего стороннего — ни ридеров, ни внешних утилит.
Install-Package FreeSpire.PDF
Ограничение бесплатной версии — до 10 страниц на документ. Для личных задач или небольших проектов этого хватает.
Базовый пример — одна страница:
PdfDocument doc = new PdfDocument();
doc.LoadFromFile("Sample.pdf");
PdfPageBase page = doc.Pages[1]; // вторая страница
PdfTextExtractor extractor = new PdfTextExtractor(page);
PdfTextExtractOptions options = new PdfTextExtractOptions { IsExtractAllText = true };
string text = extractor.ExtractText(options);
File.WriteAllText("output.txt", text);
doc.Close();
Весь документ сразу:
StringBuilder allText = new StringBuilder();
foreach (PdfPageBase page in doc.Pages)
{
var extractor = new PdfTextExtractor(page);
var options = new PdfTextExtractOptions { IsExtractAllText = true };
allText.AppendLine(extractor.ExtractText(options));
}
File.WriteAllText("output.txt", allText.ToString());
Что ещё умеет библиотека
Зашифрованные PDF открываются передачей пароля прямо в
LoadFromFile. Если нужен текст только из конкретной области страницы — задаёшь прямоугольник через ExtractArea в точках (1 point = 1/72 дюйма). Для таблиц есть отдельный
PdfTableExtractor, который возвращает данные в виде массива. Сканы и нечитаемые PDF решаются через Spire.OCR в связке с основной библиотекой.📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔4❤3
Noundry это набор инструментов и библиотек для .NET разработки. В состав входит Tuxedo, Tailbreeze, Assertive, Authnz, Engine, AI Gateway и другие компоненты.
Разработчик годами решал одни и те же задачи за корпоративными файрволами. Классическое энтерпрайз консультирование: сложные системы, высокие ставки, миллионы транзакций. Код был хороший, но никто не видел. Результат работы жил за файрволом.
Теперь он решил поделиться тем, что наработал за два десятилетия. Выложил в публичное и начал строить сообщество вокруг этого.
📍 Навигация: Вакансии • Задачи • Собесы
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7🔥3
🤔 Вопрос с собеса
Классика интервью:
На собесе часто ловят на вопросе «когда использовать». Многие говорят просто «для безопасности», но это неправильный ответ.
Правильный ответ, который ждут на собесе ждёт только вас в нашем канале с вопросами с собесов
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#dotnet_challenge
Классика интервью:
Что такое readonly struct и чем он отличается от обычной структуры
На собесе часто ловят на вопросе «когда использовать». Многие говорят просто «для безопасности», но это неправильный ответ.
Правильный ответ, который ждут на собесе ждёт только вас в нашем канале с вопросами с собесов
📍 Навигация: Вакансии • Задачи • Собесы
#dotnet_challenge
Please open Telegram to view this post
VIEW IN TELEGRAM
Вы оптимизировали базу данных. Ничего не изменилось. Оптимизировали сетевые вызовы. Всё ещё медленно.
Оказалось, приложение создаёт новый
JsonSerializerOptions в каждом запросе. Это уничтожает встроенный кеш метаданных System.Text.Json и превращает JSON сериализацию в дорогую операцию, которая повторяется сотни раз в секунду под нагрузкой.Почему это так дорого
JsonSerializerOptions это не просто настройки. Это место, где System.Text.Json хранит кэшированные метаданные о том, как сериализовать и десериализовать типы.Каждый новый экземпляр JsonSerializerOptions начинает с пустого кеша. System.Text.Json должна заново анализировать тип, строить информацию о сериализации, кешировать её. Потом запрос закончился и всё выбросилось.
Следующий запрос приходит. Новый экземпляр. Пустой кеш снова. Всё сначала.
Microsoft так серьёзно относится к этому, что добавили анализатор CA1869, который явно предупреждает: не создавайте
JsonSerializerOptions локально в горячих путях.Ошибка выглядит безобидно:
string ToJson(object value)
{
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
WriteIndented = false
};
return JsonSerializer.Serialize(value, options);
}
Под нагрузкой это выглядит как:
• CPU растёт без видимых причин
• Задержка становится нестабильной, p99 скачет
• Профилер показывает JSON сериализацию как горячую точку
• А вы не понимаете почему, если оптимизировали всё остальное
Создайте JsonSerializerOptions один раз при старте приложения и переиспользуйте везде:
public static class JsonDefaults
{
public static readonly JsonSerializerOptions Web = new(JsonSerializerDefaults.Web)
{
WriteIndented = false,
Converters = { new JsonStringEnumConverter() }
};
}
Используем кеш, никаких затрат
return JsonSerializer.Serialize(payload, JsonDefaults.Web);
Одна переменная, кеш остаётся тёплым, производительность стабильной.
📍 Навигация: Вакансии • Задачи • Собесы
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍36🔥3