.NET Разработчик
6.53K subscribers
442 photos
3 videos
14 files
2.12K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 2387. #Оффтоп
Давно не рекомендовал вам видео. А тут вчера у Дудя вышло прекрасное интервью с Андреем Дороничевым. Это своего рода сиквел популярного фильма Дудя про Кремниевую долину. Андрей с тех пор ушёл из гугла и создал стартап, где использует ИИ для решения разных больших задач (в данный момент – лекарство от рака). В интервью не только про него самого, но и 100500 "тупых вопросов" про ИИ (что он уже может и сможет в будущем, под угрозой ли наши профессии, будет ли восстание машин и т.п.), а также про личностный рост и про IT сферу вообще.

Мне очень понравилось, поэтому делюсь. Выделите 2 часа 40 минут в своём графике. Это интересно.

https://youtu.be/1SLvIof4-Zw
👎30👍17
День 2388. #ЗаметкиНаПолях
Разграничиваем Данные в Модульном Монолите. Начало
Модульные монолиты обещают производительность монолита и чёткие границы микросервисов. Каждый модуль самодостаточен: его модель предметной области, поведение и данные находятся внутри границ. Но одно из самых сложных мест для поддержания этих границ — база данных. Ничто не мешает разработчику создать JOIN-оператор между таблицами разных модулей или обойти общедоступный API.

Ранее мы рассматривали 4 уровня изоляции данных и как модули должны предоставлять явные API для доступа к своим данным. Теперь подробно рассмотрим границы в БД.

В модульном монолите каждый модуль владеет своими данными. Если модуль A обращается к таблицам модуля B, это ограничение теряется, и модули становятся тесно связанными. Вместо этого модуль B должен предоставлять общедоступный API и скрывать логику хранения своих данных от клиентов. Помимо чистого кода, соблюдение границ на уровне БД защищает вас от ошибок и упрощает последующее извлечение модуля в отдельный сервис.

Схемы БД действуют как папки: они позволяют организовывать объекты и предоставлять доступ к БД множеству пользователей, но пользователь может получить доступ к любой схеме только при наличии соответствующих привилегий. Это означает, что мы можем намеренно привязать каждый модуль к своей схеме.

Стратегия
- Создайте схему для каждого модуля и выделенную роль БД.
- Предоставьте этой роли привилегии только для её схемы и задайте для неё путь поиска по умолчанию (для PostgreSQL).
- Используйте EF с отдельными DbContext для каждого модуля, установив схему и строку подключения по умолчанию для каждого модуля.
- Для сквозных запросов создайте представление в БД, доступное только для чтения и действующее как общедоступный API.
- Необязательно: используйте политики безопасности на уровне строк для ограничения доступа внутри таблицы.
Эти методы позволят установить чёткие границы, одновременно снижая эксплуатационные издержки.

Схемы, роли и пути поиска
PostgreSQL (и многие другие БД) позволяет создавать несколько схем в одной БД. Модуль может определить свою собственную схему и роль, которой она принадлежит. Роль имеет только привилегии пользователя и уровня таблицы в этой схеме. Например, для модуля заказов:
CREATE ROLE orders_role LOGIN PASSWORD 'orders_secret';
CREATE SCHEMA orders AUTHORIZATION orders_role;
GRANT USAGE ON SCHEMA orders TO orders_role;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA orders TO orders_role;
ALTER ROLE orders_role SET search_path = orders;

Команда ALTER ROLE устанавливает путь поиска по умолчанию для роли, чтобы в запросах имена объектов, не имеющие схемы, разрешались в схему модуля. Если вы не хотите полагаться на путь поиска, используйте имена со схемой (orders.table_name).

Безопасность на уровне строк (Row-level security, RLS) позволяет фильтровать строки на основе выражения политики. RLS эффективен для многопользовательских сценариев или конфиденциальных данных, но он увеличивает сложность. Начните со схем и ролей и добавляйте RLS только при необходимости.

Окончание следует…

Источник:
https://www.milanjovanovic.tech/blog/how-to-keep-your-data-boundaries-intact-in-a-modular-monolith
👍14
День 2389. #ЗаметкиНаПолях
Разграничиваем Данные в Модульном Монолите. Окончание

Начало

Настройка схем EF и нескольких DbContext
- Используйте отдельные DbContext на каждый модуль. Установка схемы по умолчанию также влияет на последовательности и миграции.
- Укажите строку подключения для каждого модуля, используя роль модуля. Даже если модули используют одну БД, различные роли гарантируют, что неправильно настроенный контекст не сможет получить доступ к другой схеме.
- Настройте таблицу истории миграций в каждом контексте.

Пошаговая инструкция
Допустим, у нас два модуля (Orders и Shipping), и мы хотим установить границы между ними.

1. Создадим схемы и роли, предоставив каждой роли привилегии только на её схему:
-- Схема и роль Orders 
CREATE ROLE orders_role LOGIN PASSWORD 'orders_secret';
CREATE SCHEMA orders AUTHORIZATION orders_role;
GRANT USAGE ON SCHEMA orders TO orders_role;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA orders TO orders_role;
ALTER ROLE orders_role SET search_path = orders;

Аналогично для роли Shipping.

2. Добавим строки подключения:
{
"ConnectionStrings": {
"Orders": "Host=…;Database=…;Username=orders_role;Password=orders_secret",
"Shipping": "…"
}
}


3. Определим контексты для каждого модуля. Для Orders:
public class OrdersDbContext : DbContext
{
public DbSet<Order>
Orders { get; set; } = default!;


protected override void
OnModelCreating(ModelBuilder mb)
{
// схема по умолчанию
mb.HasDefaultSchema("orders");
// либо явно таблиц
mb.Entity<Order>().ToTable("orders");

base.OnModelCreating(mb);
}
}


4. Регистрируем каждый контекст со своей строкой подключения и указываем таблицу миграций:
builder.Services
.AddDbContext<OrdersDbContext>(opts =>
opts.UseNpgsql(
builder.Configuration.GetConnectionString("Orders"),
o => o.MigrationsHistoryTable("__EFMigrationsHistory", "orders")));


5. Управляем миграциями отдельно. При создании миграции указываем контекст:
dotnet ef migrations add InitialOrders --context OrdersDbContext --output-dir Data/Migrations/Orders

EF Core сгенерирует классы миграций, которые создадут таблицы в указанной схеме (благодаря HasDefaultSchema). Не забудьте применить миграции в правильном порядке при развёртывании. Вы можете автоматизировать этот процесс, используя средство выполнения миграций, итеративно перебирающее контексты.

Сквозные запросы
Даже в модульной системе иногда требуется экран, охватывающий несколько модулей, например, страница истории заказов, отображающая данные о заказе и доставке. Не поддавайтесь искушению использовать JOIN между схемами. Полезны два подхода:
1. Выделенная модель чтения. Один модуль владеет моделью представления и подписывается на события других. Этот шаблон хорошо работает, когда модули могут быть позже извлечены в микросервисы.
2. Представления в БД. Поскольку модули используют общую БД, мы можем создать в общедоступной схеме представление, доступное только для чтения, которое объединяет соответствующие таблицы. Мы предоставляем право SELECT для представления специальной роли или модулю. Это представление действует как управляемое общедоступное API. Потребители запрашивают представление, но не могут напрямую получить доступ к базовым таблицам. Однако, если впоследствии вы разделите БД, представление придётся заменить вызовом сервиса.
CREATE VIEW public.order_summary AS
SELECT o.id, o.total, s.status
FROM orders.orders o
JOIN shipping.shipments s ON s.order_id = o.id;

-- даём права чтения роли отчётов
GRANT SELECT ON public.order_summary TO reporting_role;

Это позволит создавать отчёты, не нарушая границ данных.

Источник: https://www.milanjovanovic.tech/blog/how-to-keep-your-data-boundaries-intact-in-a-modular-monolith
👍8
День 2390. #TipsAndTricks
Как не Возвращать Все Свойства в SqlRaw
В Entity Framework SqlRaw есть небольшое, иногда раздражающее ограничение: SQL-запрос должен возвращать данные для всех свойств типа сущности.
Иногда этого не нужно, поэтому давайте посмотрим, как это очень просто обойти.

Представьте, что у нас есть такой объект:
public sealed record MyEntity
{
public double? PropA { get; init; }
public double? PropB { get; init; }
}

По сути, мы указываем, что ни PropA, ни PropB не являются обязательными и, следовательно, если они отсутствуют в результирующем наборе, должны быть равны NULL. Но SqlRaw ожидает, что все свойства сущности присутствуют в предложении SELECT.

То есть:
dbContext
.Database
.SqlQuery<MyEntity>(
"SELECT PropA FROM MyTable")
.ToListAsync(token);

Завершится с ошибкой, что EF не может сопоставить запрос с типом, поскольку отсутствует PropB.

Чтобы обойти эту проблему, просто явно возвращайте PropB как NULL:
dbContext
.Database
.SqlQuery<MyEntity>(
"SELECT PropA, NULL AS PropB FROM MyTable")
.ToListAsync(token);


Вот и всё, EF снова счастлив. Конечно, это упрощённый пример, но суть, надеюсь, вы поняли.

Источник: https://steven-giesel.com/blogPost/c6bea409-9e49-4915-8529-8a8a8574ba80/how-to-not-return-all-properties-in-sqlraw
👍22
День 2391. #Архитектура
Вам Возможно не Нужен Redis

Redis, пожалуй, самая востребованная технология из всех, один из лидеров того, что вам «нужно» иметь в своём арсенале. Это очень хорошо спроектированная и впечатляющая технология. И при всём этом в ваших проектах он скорее всего не нужен!

Очень часто Redis используется просто потому, что считается отличным решением, и всё. Но при ближайшем рассмотрении реального варианта использования выясняется, что Redis ничего не улучшил и не решил основную проблему, а просто усложнил систему. Виктор Бломквист в своём блоге рассказал о трёх компаниях, в которых он работал. Везде был разный стек и разная нагрузка, но объединяло их необъяснимое стремление обязательно использовать Redis.

1. Tantan
Tantan - второе по величине приложение для знакомств в Китае. В то время, когда туда был добавлен Redis, система имела 50–100 мощных серверов БД с PostgreSQL. Каждый хранил подмножество пользовательских «свайпов», сегментированных по UserId, так что данные для конкретного пользователя хранились только на одном сервере.

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

Первая мысль - разместить эти данные в Redis. Один мощный сервер Redis должен справиться с нагрузкой, поэтому понадобится всего пара (для резервирования). Однако в процессе внедрения возник вопрос: «Почему бы просто не хранить эти данные на шардах PostgreSQL рядом со свайпами?». Сами данные были бы микроскопическими по сравнению с тем, что уже делали эти серверы. После обсуждения в команде все согласились. Добавление Redis лишь усложнило бы относительно простой стек. После развёртывания дополнительная нагрузка на серверах баз данных была едва заметна.

2. Bannerflow
Bannerflow - компания, занимающаяся рекламой в интернет. Команда разрабатывала новый набор микросервисов для настройки и публикации рекламы в социальных сетях, таких как Facebook. Команда решила добавить экземпляр Redis для кэширования. Обратите внимание, что это было для системы с нагрузкой, составляющей даже меньше 0,1% от Tantan.

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

3. MAJORITY
Финтех компания. Первым применением было кэширование результата внешнего вызова API для поиска геолокации пользователей, чтобы сервис мог обрабатывать запросы местоположения быстрее и дешевле. Кэширование этих данных вполне разумно. По чистой случайности, этот конкретный сервис выполнял два действия для поиска: вызов БД и вызов Redis. Это значительно упростило сравнение и оценку.

У сервиса была собственная БД, которая делила кластер с другими сервисами. Нагрузка на эту БД была настолько низкой, что она даже не отображалась при анализе нагрузки кластера в Azure. Перенос использования Redis в БД привёл бы к увеличению нагрузки примерно в два раза, что значительно в относительных числах, но в абсолютных это ничто, т.к. исходная была практически нулевой!

Конечно, когда в стек добавляется новая технология, всё больше и больше компонентов начинают её использовать. Так и тут. Вскоре возникла необходимость в распределении блокировок между несколькими экземплярами одного микросервиса. Т.к. Redis уже использовался, было естественно использовать его и для этой цели.

Но при более внимательном рассмотрении стало легко увидеть, что для этих блокировок можно было бы использовать механизм блокировок внутри основной БД (Azure SQL). Это бы увеличило нагрузку на БД, но, как и в предыдущем примере, это не было высокопроизводительным вариантом использования.

Источник: https://www.viblo.se/posts/no-need-redis/
👍10
День 2392. #ЗаметкиНаПолях
PDF из HTML в .NET (бесплатно). Начало

Создание качественных PDF-отчётов в .NET не обязательно должно быть мучением. Тем, кто хоть немного работает с HTML, проще будет использовать преобразование из HTML в PDF.

Но многие популярные библиотеки требуют коммерческой лицензии. В этой серии рассмотрим бесплатный подход с использованием:
- Handlebars.NET для шаблонов,
- PuppeteerSharp для рендеринга.

Это даёт вам полный контроль над макетом, стилем и содержимым:
- Богатая стилизация с помощью CSS,
- Легкий просмотр/отладка в браузере,
- Поддержка диаграмм/изображений через JS/CSS,
- Полный контроль над макетом (медиазапросы, разрывы страниц и т. д.).

Минусы:
- Требуется установка браузера (например, Chromium),
- Медленнее, чем нативные библиотеки PDF,
- Немного сложнее в настройке.

1. Настройка
Создадим проект минимальных API и установим NuGet-пакеты:
Install-Package Handlebars.Net
Install-Package PuppeteerSharp


2. Создание шаблона
Это простой HTML-документ с несколькими заместителями для Handlebars. Вы заметите их по синтаксису {{variable}}. Они будут заменены реальными данными при рендеринге шаблона.
<!-- Templates/InvoiceTemplate.html -->
<html lang="en">
<head>
<style>
body {
font-family: Arial;
}
</style>
</head>
<body>
<h1>Invoice #{{Number}}</h1>

<p>Date: {{formatDate IssuedDate}}</p>

<h2>From:</h2>
<p>{{SellerAddress.CompanyName}}</p>
<p>{{SellerAddress.Email}}</p>

<h2>To:</h2>
<p>{{CustomerAddress.CompanyName}}</p>
<p>{{CustomerAddress.Email}}</p>

<h2>Items:</h2>
<table>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
{{#each LineItems}}
<tr>
<td>{{Name}}</td>
<td>{{formatCurrency Price}}</td>
</tr>
{{/each}}
</table>

<p><strong>Total: {{formatCurrency Total}}</strong></p>
</body>
</html>


Вызовы функций, вроде {{formatDate IssuedDate}} – это пользовательские вспомогательные функции, которые можно определить в Handlebars. Они регистрируются так:
Handlebars.RegisterHelper("formatDate", (context, arguments) =>
{
if (arguments[0] is DateOnly date)
return date.ToString("dd/MM/yyyy");
return arguments[0]?.ToString() ?? "";
});


Это позволяет, например, форматировать даты, валюты и т.п. при необходимости. Вспомогательные функции регистрируются перед компиляцией шаблона (его мы рассмотрим в продолжении), достаточно одного раза при запуске приложения.
Кроме того, в Handlebars есть встроенные управляющие конструкции, например:
- {{#if …}} ... {{/if}} для условий,
- {{#each …}} ... {{/each}} для перебора коллекций.

Продолжение следует…

Источник:
https://www.milanjovanovic.tech/blog/pdf-reporting-in-dotnet-with-html-templates-and-puppeteersharp
👍9
День 2393. #ЗаметкиНаПолях
PDF из HTML в .NET (бесплатно). Продолжение

Начало. Создание шаблона

3. Рендеринг шаблона
Мы используем Handlebars для компиляции шаблона с данными, а затем PuppeteerSharp для рендеринга в PDF.

Сначала прочитаем файл шаблона, скомпилируем его с помощью Handlebars и заполним данными:
var tmplt = File.ReadAllText(
"Templates/InvoiceTemplate.html");

var data = new {
IssuedDate = new DateOnly(2025, 08, 19),
Number = 12345,

LineItems = new[] {
new { Name = "Software License", Price = 99d },
new { Name = "Support Plan", Price = 49d }
}
};

// ... регистрируем вспомогательные функции Handlebars до вызова компиляции ...

var compiledTmplt = Handlebars.Compile(tmplt);

var html = compiledTmplt(data);

Это даст нам финальный HTML-код, в котором все заместители заменены реальными данными.

Примечание: не обязательно использовать Handlebars. Можно использовать любой шаблонизатор, например, Razor или Scriban.

4. Преобразование в PDF
Мы запустим браузер без UI, установим получившийся HTML код как его контент и сгенерируем PDF-файл:
// Скачиваем бинарные файлы браузера 
var browserFetcher = new BrowserFetcher();
await browserFetcher.DownloadAsync();

// Запускаем браузер и создаём страницу
using var browser = await Puppeteer.LaunchAsync(
new LaunchOptions { Headless = true });
using var page = await browser.NewPageAsync();

// Устанавливаем контент страницы
await page.SetContentAsync(html);

// Не обязательно: ждём загрузки шрифтов
await page.EvaluateExpressionHandleAsync(
"document.fonts.ready");

// Настраиваем PDF
byte[] pdf = await page.PdfDataAsync(
new PdfOptions {
Format = PaperFormat.A4,
PrintBackground = true,
MarginOptions = new MarginOptions
{
Top = "50px",
Right = "20px",
Bottom = "50px",
Left = "20px"
}
});

В начале нам нужно скачать движок Chromium для рендеринга HTML страницы. PuppeteerSharp требует загрузки исполняемых файлов браузера Chromium во время выполнения. Это можно сделать, вызвав BrowserFetcher.DownloadAsync(). Это займёт какое-то время в первый раз, далее PuppeteerSharp будет использовать скачанные бинарные файлы.

В итоге в переменной pdf мы получим массив байтов, содержащий данные PDF-файла. Вы можете сохранить его в файл или вернуть из конечной точки API. Например, так:
return Results.File(pdf, "application/pdf", "invoice.pdf");


Окончание следует…

Источник:
https://www.milanjovanovic.tech/blog/pdf-reporting-in-dotnet-with-html-templates-and-puppeteersharp
👍6
День 2394. #ЗаметкиНаПолях
PDF из HTML в .NET (бесплатно). Окончание

Создание шаблона
Рендеринг в PDF

5. Улучшения: изображения, верхний/нижний колонтитул, стили
Можно добавлять изображения в шаблон с помощью тега <img>. Простой подход — использовать изображение в кодировке base64 непосредственно в HTML-коде. Обратите внимание, что мы передаём данные изображения через переменную LogoBase64:
<img
src="data:image/png;base64,{{LogoBase64}}"
alt="Logo"
style="height:50px; max-width:200px; object-fit:contain;"
/>

Мы также можем отображать динамические верхние и нижние колонтитулы, используя встроенную поддержку PuppeteerSharp. Можно определить их в объекте PdfOptions при создании PDF-файла:
var pdfOptions = new PdfOptions
{
HeaderTemplate =
@"""
<div style='font-size: 14px; text-align: center; padding: 10px;'>
<span style='margin-right: 20px;'><span class='title'></span></span>
<span><span class='date'></span></span>
</div>
""",
FooterTemplate =
@"""
<div style='font-size: 14px; text-align: center; padding: 10px;'>
<span style='margin-right: 20px;'>Документ создан <span class='date'></span></span>
<span>Страница <span class='pageNumber'></span> из <span class='totalPages'></span></span>
</div>
""",
DisplayHeaderFooter = true
};

PuppeteerSharp использует CSS-классы, такие как title, date, pageNumber и totalPages для внедрения динамических значений. В некоторых других библиотеках это может отличаться, поэтому сверьтесь с документацией.

Наконец, вы можете использовать CSS для расширенной стилизации. CSS может быть встроен в HTML-код или оформлен в отдельный CSS-файл. При необходимости вы также можете ссылаться на внешние таблицы стилей, используя тег <link>.

6. Вопросы производительности
Первое, что следует рассмотреть, — это использование headless-браузера, например, Chromium. Он может работать медленнее, чем нативные PDF-библиотеки, особенно для больших документов или высокого уровня параллелизма. Кроме того, это увеличивает нагрузку на процессор, поскольку необходимо загружать и запускать исполняемые файлы браузера. Однозначно стоит рассмотреть возможность вынесения этого процесса из основного приложения. Вы можете использовать фоновый сервис или отдельный микросервис для генерации PDF-файлов. Облачная функция может быть хорошим решением при масштабировании. Но всё зависит от вашего конкретного сценария использования.

Итого
HTML + PuppeteerSharp — один из самых практичных подходов к созданию PDF-отчётов в .NET. Он позволяет:
- Создавать безупречные макеты с использованием знакомых веб-технологий,
- Вставлять динамические данные с помощью Handlebars,
- Выводить высококачественные PDF-файлы с полной стилизацией, таблицами и изображениями.
И всё это без использования коммерческих библиотек. По сравнению с платными библиотеками, вроде QuestPdf или IronPdf, вы пожертвуете производительностью и придётся выполнить некоторую первоначальную настройку. Но для большинства внутренних инструментов, отчётов или генерации счетов для клиентов это того стоит.

Источник: https://www.milanjovanovic.tech/blog/pdf-reporting-in-dotnet-with-html-templates-and-puppeteersharp
👍7
День 2395. #SystemDesign101
Как Работает SSO?

Технология единого входа (Single Sign-On SSO) — технология, позволяющая пользователю аутентифицироваться один раз, чтобы получить доступ ко множеству связанных сервисов или приложений, без необходимости повторно вводить свои учётные данные для каждого из них.

Рассмотрим типичный процесс входа в SSO.
Шаг 1: Пользователь запрашивает защищённый ресурс в приложении, например, Gmail, которое является провайдером сервиса (Service Provider, SP).

Шаг 2: Сервер Gmail обнаруживает, что пользователь не вошёл в систему, и перенаправляет браузер к провайдеру идентификации (Identity Provider, IdP) компании с запросом на аутентификацию.

Шаг 3: Браузер перенаправляет пользователя к IdP.

Шаг 4: IdP отображает страницу входа, где пользователь вводит свои учётные данные.

Шаг 5: IdP создаёт защищённый токен и возвращает его браузеру. IdP также создаёт сессию для будущего доступа. Браузер перенаправляет токен в Gmail.

Шаг 6: Gmail проверяет токен, чтобы убедиться, что он получен от провайдера идентификации.

Шаг 7: Gmail возвращает защищённые ресурсы в браузер в зависимости от того, к чему пользователю разрешён доступ.

На этом процесс входа с помощью SSO завершается. Теперь посмотрим, что произойдёт, когда пользователь перейдёт в другое приложение с интегрированной системой единого входа, например, в Slack.

Шаги 8-9: Пользователь заходит в Slack, и сервер Slack обнаруживает, что он не вошёл в систему. Slack перенаправляет браузер к провайдеру идентификации с новым запросом на аутентификацию.

Шаг 10: Браузер перенаправляет пользователя к провайдеру идентификации.

Шаги 11-13: Поскольку пользователь уже вошёл в систему провайдера идентификации, процесс входа пропускается и сразу создаётся новый токен для Slack. Новый токен отправляется в браузер, который перенаправляет его в Slack.

Шаг 14–15: Slack проверяет токен и предоставляет пользователю соответствующий доступ.

Источник: https://blog.bytebytego.com/
👍20
День 2396. #ЧтоНовенького #NET10
Ещё Два Метода-Расширения LINQ в .NET 10

В предварительной версии 6 .NET 10 появились ещё два метода LINQ: InfiniteSequence и Sequence.

1. InfiniteSequence
InfiniteSequence — это простой генератор, позволяющий получить бесконечную последовательность из начальной точки, задавая размер шага:
var steps = 
Enumerable.InfiniteSequence(0, 5)
.Take(10)
.ToList();

foreach (var step in steps)
Console.WriteLine(step);

В результате будут выведены значения 0, 5, 10, 15, …, 45.
Замечание: будьте осторожны и не вызывайте ToList/Count и подобные функции без Take, иначе вы получите исключение «Недостаточно памяти».

2. Sequence
Это почти то же самое, что InfiniteSequence, с той разницей, что можно определить включительно верхнюю границу:
var steps = 
Enumerable.Sequence(0, 45, 5)
.ToList();

foreach (var step in steps)
Console.WriteLine(step);

Результат будет таким же, как в предыдущем примере.

Источник: https://steven-giesel.com/blogPost/5d88d808-03ac-431a-82fa-756b59b38a7d/two-more-linq-extensions-in-dotnet-10
👍28
День 2397. #ЗаметкиНаПолях
Сокращаем Шаблонный Код и Поддерживаем Согласованность Проекта. Начало


TL/DR: Если вы работаете в небольшой компании и ведёте всего один или два проекта, эта публикация может быть для вас неактуальна.

В крупных могут быть сотни или тысячи репозиториев. По мере роста организации поддерживать согласованность и единообразие всех проектов становится сложной задачей. Легко использовать шаблон для запуска нового проекта, но как поддерживать согласованность всех проектов с течением времени? Как гарантировать, что все проекты обновляют свои стандарты кодирования, CI/CD, безопасность, наблюдаемость и другие аспекты в соответствии с передовыми практиками? Каждая компания сталкивается с уникальными проблемами, но есть некоторые идеи и стратегии, которые помогут повысить согласованность между проектами.

В первую очередь необходимо по максимуму удалить шаблонный код и конфигурации из каждого проекта и заменить их неявными значениями по умолчанию или ссылками, которые можно обновлять с помощью таких инструментов, как Dependabot или Renovate. Такой подход обеспечивает базовую конфигурацию, которая может обновляться автоматически. Также важно предусмотреть точки расширения для настроек, специфичных для проекта.

Преимущества
- Сосредоточенность на продукте: разработчики могут сосредоточиться на разработке функций, а не на настройке и поддержке общего кода.
- Более эффективное организационное воздействие: каждый дополнительный проект, ссылающийся на общую библиотеку, увеличивает её ценность.
- Ускоренная поставка: повторное использование существующих компонентов ускоряет переход от идеи к производству.
- Единообразие: способствует внедрению единых практик и стандартов во всех проектах.
- Сокращение затрат на обслуживание: меньше дублирующегося кода означает меньше задач по поддержке и тестированию.
- Расширенное сотрудничество: способствует обмену знаниями и командной работе.
- Меньше шаблонного кода: кодовые базы легче читать и поддерживать, поскольку не нужно повторять одну и ту же конфигурацию в нескольких проектах. Кроме того, благодаря сокращению шаблонного кода становится понятнее, что относится к конкретному проекту.
- Обновляемость: предоставление новых функций или исправление ошибок без необходимости внесения изменений в каждый проект.

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

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

Вам также необходимо понимать, что речь идёт не об универсальных библиотеках, которые может использовать каждый. Речь идёт о создании общих компонентов, специфичных для вашей компании и проектов. Это, безусловно, субъективный подход, поскольку вы хотите предоставить разработчикам правильные настройки по умолчанию, чтобы помочь им добиться успеха.

Далее мы рассмотрим несколько видов кода и конфигураций, которыми можно делиться по ссылке. Некоторые из них специфичны для .NET, но вообще эти принципы применимы к любому технологическому стеку.

Продолжение следует…

Источник:
https://www.meziantou.net/reduce-boilerplate-and-maintain-project-consistency.htm
👍9👎1
День 2398. #ЗаметкиНаПолях
Сокращаем Шаблонный Код и Поддерживаем Согласованность Проекта. Продолжение

Начало

1. Стандарты кодирования .NET и статический анализ
Идея заключается в создании NuGet-пакета, содержащего файлы .editorconfig, анализаторы и конфигурацию MSBuild. Этот пакет предоставляет:
- Единый стиль кодирования для всех проектов, которые на него ссылаются;
- Набор анализаторов Roslyn и базовую конфигурацию для обеспечения статического анализа в соответствии с передовыми практиками и требованиями безопасности;
- Свойства, элементы и таргеты MSBuild для настройки проекта, такие как включение обнуляемых ссылочных типов, применение стиля кода при сборке, обработка предупреждений как ошибок в непрерывной интеграции, включение аудита NuGet, включение ссылок на исходный код и многое другое.

Этот пакет позволяет удалить сотни строк шаблонного кода и конфигурации из каждого проекта:
- Разделы C# и VB.NET из файла .editorconfig (~100 строк);
- Файлы конфигурации, такие как Directory.Build.props и Directory.Build.targets (~70 строк);
- Конфигурацию анализатора (до нескольких сотен строк).

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

В качестве бонуса можно написать тесты, чтобы убедиться, что все настройки работают как надо. Некоторые тесты вы найдёте в исходном коде пакета.

Можно добавлять некоторые функции в стандарт кодирования, например, запретить использование типов Newtonsoft.Json в проектах. Для этого достаточно добавить новое свойство в файл проекта:
<PropertyGroup>
<BanNewtonsoftJsonSymbols>true</BanNewtonsoftJsonSymbols>
</PropertyGroup>


Теперь у вас есть единое место для предоставления функций разработчикам, и его можно обновлять под ваши требования. Разработчики могут сосредоточиться на написании кода, а не на настройке своих проектов: применяйте правила, написанные в пакете, и всё.

Если вы используете .NET, настоятельно рекомендую почитать про MSBuild. А использование пакетов SDK для MSBuild открывает множество дополнительных возможностей.

2. Конфигурация Renovate
Renovate — это инструмент для автоматизации обновления зависимостей. Его можно настроить с помощью файла renovate.json. Файл конфигурации может быть довольно большим, особенно если у вас много зависимостей или вы хотите настроить поведение Renovate. Это может привести к дублированию данных в нескольких проектах, если у каждого проекта будет свой файл renovate.json. Кроме того, каждая команда может настраивать Renovate по-своему, что приводит к несоответствиям. Например, команды могут использовать разные форматы заголовков пул-реквестов, что затрудняет создание правил для их фильтрации.

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

Лучшее решение — создать общий файл конфигурации Renovate и ссылаться на него в каждом проекте. Вот пример файла. Также в GitHub Renovate автоматически ссылается на общую конфигурацию, если она находится в той же организации, а репозиторий называется renovate-config. Это означает, что вы можете удалить файл renovate.json из каждого проекта и позволить Renovate использовать общую конфигурацию, если вам не нужно её переопределять.

Продолжение следует…

Источник:
https://www.meziantou.net/reduce-boilerplate-and-maintain-project-consistency.htm
👍11
День 2399. #ЗаметкиНаПолях
Сокращаем Шаблонный Код и Поддерживаем Согласованность Проекта. Продолжение

Начало
Продолжение

3. Пакет ServiceDefaults
При создании нескольких веб-API или рабочих процессов вам может потребоваться повторять одну и ту же конфигурацию для каждого проекта. Например, потребуется добавить Azure AppConfiguration, настроить OpenTelemetry, логирование, проверки работоспособности, и т.п. Вместо того, чтобы дублировать эту конфигурацию во всех проектах, вы можете создать общий пакет ServiceDefaults, содержащий общую конфигурацию, и использовать его в каждом проекте. Главное преимущество — возможность лёгкого обновления с течением времени. Если вам нужно изменить конфигурацию OpenTelemetry, вы можете сделать это в пакете, и все проекты, которые на него ссылаются, обновятся.

Если вы знакомы с шаблоном .NET Aspire, вы, возможно, видели проект ServiceDefaults. Он предоставляет набор общих сервисов и конфигураций, которые можно использовать повторно в нескольких проектах в рамках одного решения. Но можно ведь создать NuGet-пакет, который можно использовать в любом проекте.
Пример проекта пакета на GitHub.

4. Задачи MSBuild
MSBuild — это расширяемая система сборки, позволяющая создавать пользовательские задачи и таргеты. На сайте nuget.org представлено несколько пакетов для добавления задач MSBuild в ваш проект. Например, вы можете автоматически устанавливать версию своего проекта с помощью MinVer, GitVersion.MsBuild или Nerdbank.GitVersioning. А, например, пакет Workleap.OpenApi.MSBuild интегрируется в процесс сборки (dotnet build), чтобы гарантировать актуальность файла спецификации OpenAPI, соответствие коду, стандартам компании и отсутствие критических изменений в API.

5. Фрагменты файлов
Иногда удалить шаблонный код невозможно. В этом случае стратегия заключается в том, чтобы поместить его в «область», которую можно легко идентифицировать и обновить. Версионирование и обновление общей части файла можно осуществлять с помощью такого инструмента, как Renovate.

Общие части файлов заключаются в комментарии # reference:<URL> и # endreference. Таким образом, вы можете легко найти общую часть файла и обновить её при необходимости. Версию общей части файла можно контролировать с помощью тегов и обновлять с помощью Renovate. Обратите внимание, что Renovate позволяет запускать пользовательские инструменты после обновления ссылки.
 
# reference:https://…/.editorconfig
root = true

[*]
indent_style = space
trim_trailing_whitespace = true
end_of_line = lf

# endreference

# Настройки специфичные для проекта
[*.cs]
dotnet_diagnostic.CA1008.severity = none


6. Конвейеры CI/CD
Конвейеры — ещё один хороший вариант для совместного использования между проектами. Существуют различные стратегии совместного использования конвейеров.
Комплаенс-конвейеры (например, SAST, DAST, сканирование зависимостей и т.д.) можно перенести на уровень организации. Это гарантирует, что все проекты будут иметь одинаковые проверки соответствия, и вы сможете обновлять их в одном месте. Это также избавит от необходимости обновлять каждый проект при изменении проверок соответствия.
GitHub предоставляет гибкие способы совместного использования частей конвейеров CI/CD:
- композитные действия
- настраиваемые действия
- повторно используемые рабочие процессы

Окончание следует…

Источник:
https://www.meziantou.net/reduce-boilerplate-and-maintain-project-consistency.htm
День 2400. #ЗаметкиНаПолях
Сокращаем Шаблонный Код и Поддерживаем Согласованность Проекта. Окончание

Начало
Продолжение 1-2
Продолжение 3-5

7. Dockerfile
Вам действительно нужен Dockerfile? Некоторые технологии, такие как .NET, позволяют создавать образы Docker без Dockerfile. Например, можно использовать dotnet publish для создания образа и его отправки в реестр. Это не только избавляет от шаблонного кода, но и повышает производительность. Это также позволяет избежать многих распространённых проблем с Dockerfile, таких как кэширование.
# Dockerfile не нужен
dotnet publish -p:PublishProfile=DefaultContainer


8. Helm-чарты
Поскольку вы можете устанавливать стандарты с помощью пакетов MSBuild или SDK, вы можете повторно использовать эти стандарты в Helm-чартах. Например, вы можете настроить проверки готовности и жизнеспособности в общем чарте. Вы также можете добавить параметры, специфичные для вашей инфраструктуры, такие как сертификаты, автоматическое масштабирование, идентификацию пода Azure и т.д. Таким образом, вы можете удалить шаблонный код из Helm-чартов и сделать их более пригодными для повторного использования в разных проектах. В большинстве проектов требуется задать имя образа, ограничения на ресурсы ЦП и памяти. Остальные параметры должны быть общими для большинства проектов.

9. Модули PowerShell
PowerShell — мощный язык сценариев, который можно использовать для автоматизации задач и управления системами. Он очень распространён в непрерывной интеграции (CI) или для локальных операций. Модули PowerShell — отличный способ совместного использования скриптов и функций в разных проектах. Вы можете создать модуль, содержащий общие функции и скрипты, которые можно использовать повторно в нескольких проектах, используя команду
Import-Module -Name <ModuleName> -RequiredVersion <ModuleVersion>

Модули имеют версии и публикуются как NuGet-пакеты, поэтому вы можете легко обновлять их при необходимости:
- Publish-Module
- Install-Module

Обновление существующих проектов
Предоставление общих библиотек и конфигурации — это здорово, но для их использования также необходимо обновить существующие проекты. Это может быть непросто, поскольку вам, возможно, придётся обновлять сотни проектов. Лучший способ сделать это — создать инструмент, который будет автоматически это делать. Вот пример кода такого инструмента. Он клонирует все репозитории и применяет к ним миграцию. Затем создаёт пулл-реквест с изменениями. Некоторые миграции просты и могут быть написаны детерминированным способом. Для более сложных миграций скрипт миграции может использовать ИИ для помощи.

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

Итого
Просмотрите свои проекты и определите, есть ли что-то ещё, что можно использовать по ссылке. Например:
- Конфигурациями IDE (например, .vscode, vsconfig для Visual Studio)
- Git-хуки
- Инструменты разработки, необходимые для запуска проекта (например, dev-контейнеры, GitHub Codespaces, devbox и т. д.). Фактически, обратите внимание на всё, что не является бизнес-кодом, и подумайте, можно ли удалить это из проекта.

Источник:
https://www.meziantou.net/reduce-boilerplate-and-maintain-project-consistency.htm
👍4
День 2401. #ЗаметкиНаПолях
Разница Между Выражениями и Инициализаторами Коллекций

Вы когда-нибудь задумывались, есть ли разница между выражением коллекции:
List<int> list = [1, 2, 3];

и инициализатором коллекции:
List<int> list2 = new() {1,2,3};


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

Выражение коллекции:
int num = 3;
List<int> list = new List<int>(num);
CollectionsMarshal.SetCount(list, num);
Span<int> span = CollectionsMarshal.AsSpan(list);
int num2 = 0;
span[num2] = 1;
num2++;
span[num2] = 2;
num2++;
span[num2] = 3;
num2++;


Инициализатор коллекции:
List<int> list2 = new List<int>();
list2.Add(1);
list2.Add(2);
list2.Add(3);


Таким образом, выражения для коллекций ([1,2,3]) «умнее» и быстрее в том смысле, что они заранее выделяют список с точным количеством элементов, которые мы хотим добавить. Инициализатор коллекции не делает этого по одной простой причине: компилятор, согласно своей спецификации, обязан вызывать метод Add для каждого элемента в инициализаторе.

Конечно, для 3х элементов особой разницы в производительности не будет. А вот, например, бенчмарк для 17 элементов:
|    Method |     Mean | Allocated |
|-----------|---------:|----------:|
|Expression | 18.09 ns | 128 B |
|Initializer| 72.88 ns | 368 B |

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

Источник: https://steven-giesel.com/blogPost/fea0b033-ccf5-4197-b62c-ffd8ca6d79c7/quick-one-difference-between-collection-expressions-and-collection-initializer
👍41
День 2402. #ЧтоНовенького #NET10
Новинки в .NET 10 Превью 7.
ASP.NET Core

1. В промежуточное ПО обработки исключений ASP.NET Core добавлен новый параметр конфигурации для управления диагностическим выводом ExceptionHandlerOptions.SuppressDiagnosticsCallback. Этот метод обратного вызова получает контекст запроса и исключения, позволяя добавить логику, определяющую, должно ли промежуточное ПО записывать логи исключения и другие телеметрические данные.
Это полезно, когда вы знаете, что исключение является временным или уже где-то обрабатывается, и вы не хотите, чтобы логи этого исключения засоряли ваш дашборд.
Кроме того, изменилось поведение промежуточного ПО обработки исключений по умолчанию: диагностические данные для исключений, обрабатываемых IExceptionHandler, больше не записываются. Согласно отзывам пользователей, логирование обработанных исключений на уровне ошибок часто было нежелательным, если IExceptionHandler.TryHandleAsync возвращал true. Вернуть предыдущее поведение можно, добавив следующий код:
app.UseExceptionHandler(new ExceptionHandlerOptions
{
SuppressDiagnosticsCallback = context => false;
});


2. По умолчанию неаутентифицированные и неавторизованные запросы к известным конечным точкам API, защищенным аутентификацией через cookie, теперь приводят к ответам 401 и 403, а не к перенаправлению на URI входа в систему. Такое перенаправление обычно не имеет смысла для API.
Конечные точки API идентифицируются с помощью нового интерфейса IApiEndpointMetadata, и его реализация была автоматически добавлена для следующих конечные точек:
- в [ApiController];
- в минимальных API, принимающих JSON в теле запроса или выдающих JSON-ответы;
- использующих типы возврата TypedResults;
- в SignalR.
Если вы хотите предотвратить это новое поведение, вы можете переопределить события RedirectToLogin и RedirectToAccessDenied при вызове builder.Services.AddAuthentication().AddCookie(…).

3. Упрощены и расширены API аутентификации по ключу доступа (passkey) в шаблонах веб-приложений Blazor, что упрощает реализацию сценариев входа без пароля. Можно попробовать новый шаблон приложения Blazor с поддержкой ключа доступа:
dotnet new blazor -au Individual


4. ASP.NET Core теперь полностью поддерживает домен верхнего уровня .localhost, что позволяет разработчикам запускать несколько локальных приложений с более чётким разделением доменов. Встроенный веб-сервер Kestrel распознаёт адреса .localhost как локальные (loopback), обеспечивая безопасную и согласованную локальную разработку.

5. MVC, Minimal API и методы HttpRequestJsonExtensions.ReadFromJsonAsync были обновлены для поддержки PipeReader в System.Text.Json, что не требует внесения каких-либо изменений в код приложений.
Для большинства приложений это не должно повлиять на поведение. Однако, если приложение использует кастомный JsonConverter, существует вероятность, что конвертер будет некорректно обрабатывать Utf8JsonReader.HasValueSequence. Это может привести к потере данных и возникновению ошибок, таких как ArgumentOutOfRangeException, при десериализации.
Быстрый способ решения проблемы (особенно если вы не являетесь владельцем используемого кастомного JsonConverter) — установить переключатель AppContext "Microsoft.AspNetCore.UseStreamBasedJsonParsing" в значение true. Это должно быть временным решением, а JsonConverter(ы) следует обновить для поддержки HasValueSequence.

6. Теперь можно использовать атрибуты валидации как для классов, так и для записей, что обеспечивает единообразие генерации кода и поведения валидации. Это повышает гибкость при проектировании моделей с использованием записей в приложениях ASP.NET Core.

Источник: https://www.infoq.com/news/2025/08/dotnet-10-preview-7/
👍9
День 2403. #TipsAndTricks
Git Worktree: Управление Несколькими Рабочими Каталогами

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

Git Worktree создаёт дополнительные рабочие папки (деревья), связанные с тем же репозиторием. Каждое рабочее дерево может иметь извлекаемую ветку, что позволяет работать над несколькими функциями, исправлением ошибок или экспериментами без необходимости многократного клонирования всего репозитория. Главное преимущество заключается в том, что у вас есть только один индекс Git (каталог .git), что снижает использование дискового пространства и повышает производительность.

В процессе написания кода переключение контекстов может мешать работе. Рабочее дерево Git помогает:
- Сохранять контекст. Сохраняйте текущую работу без изменений, быстро переключаясь между исправлением ошибки или проверкой кода.
- Работать параллельно. Работайте над несколькими функциями одновременно без накладных расходов на переключение веток.
- Использовать ИИ-агента. Вы можете поручить ИИ-агенту работать над отдельной веткой, пока вы продолжаете выполнять текущие задачи.
- Снижать когнитивную нагрузку. Не нужно откладывать или фиксировать незавершенную работу при переключении задач.
- Сравнивать файлы. Легко сравнивайте изменения между ветками без необходимости переключаться между ними. Вы можете открыть несколько версий одного и того же файла в разных рабочих деревьях, что упрощает отслеживание того, как изменения влияют на ваш код.

Чтобы создать новое рабочее дерево:
# Рабочее дерево для существующей ветки
git worktree add ../feature-branch feature-branch

# Рабочее дерево для новой ветки из main
git worktree add -b new-feature ../new-feature origin/main

Чтобы удалить рабочее дерево, можно использовать следующие команды:
# Удалить рабочее дерево
git worktree remove ../feature-branch

# Удалить директорию и очистить метаданные
rm -rf ../feature-branch
git worktree prune


Также можно получить список рабочих деревьев:
git worktree list


Рекомендации
- Храните рабочие деревья в предсказуемом месте (например, в каталогах на одном уровне с репозиториями).
- Используйте описательные имена каталогов, соответствующие названиям веток.
- Регулярно очищайте неиспользуемые рабочие деревья, чтобы избежать беспорядка.

Итого
Рабочее дерево Git — это малоиспользуемая функция, которая может значительно улучшить процесс разработки, особенно когда вам нужно одновременно поддерживать несколько контекстов.

Источник: https://www.meziantou.net/git-worktree-managing-multiple-working-directories.htm
👍16
День 2404. #Карьера
8 Правил Прохождения Техсобеса. Начало
Советы от шведского разработчика/архитектора ПО Виктора 'viblo' Бломквиста. Советы, основаны на его опыте работы и собеседований на «обычные» должности разработчиков в Европе, особенно в Швеции, в небольших и средних технологических компаниях, где интервьюеры не стремятся вас унизить или подшутить над вами. Думаю, они подойдут и для многих наших компаний.

1. Практика — залог успеха
Главный совет - сначала попрактиковаться! Не в смысле сотни часов на LeetCode или заучивания документации по Kubernetes. В смысле «тренировочных собеседований», чтобы освоиться в обстановке собеседования. Многие (большинство?) разработчики просто не очень комфортно чувствуют себя на собеседованиях, и практика действительно помогает. Совершенно нормально нервничать, запинаться и т.п. Но нужно уметь рассказать интервьюеру, что вы знаете! Я как-то не смог рассказать, какими способами можно передавать данные между разными процессами. Да, просто нервничал и был неудачный день. Но при этом, несмотря на солидный опыт и резюме, такого кандидата сложно рекомендовать к найму, независимо от других качеств.
В начале карьеры собеседования – это стресс. А мы не любим делать то, что вызывает стресс, поэтому боимся практиковаться. Не повторяйте эту ошибку! Практика — залог успеха!

2. Резюме. Много коротких работ
У некоторых кандидатов много коротких работ (например, 3-5 работ продолжительностью год или меньше). Это не критично, и скорее всего, вас пригласят на собеседование в любом случае. Но вам нужно иметь веские аргументы, почему на этой работе будет по-другому. Если, конечно, компания специально не ищет человека на короткий срок. Продумайте свои доводы заранее. И, вероятно, лучше упомянуть об этом до того, как вас об этом спросят.

3. Резюме. Изменения ролей
Если ваша текущая должность отличается от той, на которую вы претендуете, необходимо это осветить и объяснить. Это также важно и тогда, когда вы претендуете на более «низкую» должность. Один из типичных случаев — кандидаты, которые сейчас возглавляют небольшую команду, но подают заявку на должность разработчика. Такие кандидаты и их опыт могут быть очень ценными, особенно при найме на должность старшего разработчика в небольшой компании, где от разработчиков ожидается больше ответственности, чем просто выполнение заранее спланированной задачи. В то же время есть несколько вещей, в которых вам следует убедить интервьюеров:
- что у вас всё ещё есть технические навыки. Руководители команд нередко теряют связь с технологиями, или, возможно, изначально не были так увлечены ими;
- объяснить, почему вы хотите вернуться к программированию на постоянной основе. То, что вы не можете найти работу тимлидом – не очень хорошее объяснение.

4. Не говорите слишком много
Особенно если вы в чём-то не уверены, длительное рассуждение не поможет. Звучит очевидно, правда? Но, видимо, на практике про это забывают! Бывают кандидаты, которые могут дать достойный ответ на простой вопрос, но затем заваливают свой собственный ответ, пускаясь в рассуждения.
Связанная с этим проблема: вы, как ни парадоксально, можете не дать интервьюеру достаточно узнать о вас. Часто лучший результат технического вопроса — это обсуждение с интервьюером(-ами) на равных. Это возможно только в том случае, если вы позволите интервьюеру говорить и не будете уходить в десятиминутный монолог на заданную тему. Это особенно важно, если у вас нет глубокого опыта в этой теме или вы не умеете хорошо выступать.

Окончание следует…

Источник:
https://www.viblo.se/posts/interviewing/
👍17👎2
День 2405. #Карьера
8 Правил Прохождения Техсобеса. Окончание

Начало

5. Устаревший и неактуальный опыт
Как кандидату с опытом, вам нужно быть очень осторожным, говоря об устаревших/неактуальных технологиях или опыте, который не соответствует тому, что ищет интервьюер. Проблема не в том, что у вас есть такой опыт, а в том, что интервьюер опасается, что вы не знаете или не любите то, что требуется, и что вы не сможете адаптироваться к технологиям и процессам новой компании.
Например, ваши навыки написания скриптов для Linux могут быть всё ещё актуальны для компании, работающей с собственным Kubernetes, но нужно это объяснить! Не зацикливайтесь на том, как вы написали скрипт для установки 100 физических машин, потому что в данном контексте это бесполезно. Вместо этого вы можете рассказать о том, как глубокие знания Linux помогают вам писать оптимизированные и безопасные Docker-файлы или устранять сетевые неполадки при запуске в под.

6. Признавайте свои ограничения (но не слишком!)
Большинство кандидатов хорошо осознают свои возможности и знают свои границы. Это, в общем и целом, хорошо. В большинстве случаев, когда человек говорит о теме, в которой он не очень хорошо разбирается, это и так очевидно, так что можно прямо признать это. В то же время, не будьте слишком скромными и не нужно выставлять напоказ все свои слабости!

7. Примеры
Какой бы ни была тема, всегда полезно иметь возможность обратиться к реальному опыту. Будет здорово собрать несколько хороших примеров перед собеседованием — стоит подготовиться!
Например, вас спрашивают о вашем опыте в области событийно-управляемой архитектуры, но вы пока мало что сделали в этой области. Тем не менее, однажды вы внедрили очередь работ. Расскажите об этом и объясните, что вы сделали, как и почему. Другой вариант — вспомнить случай, когда вы рассматривали возможность использования событийно-управляемой архитектуры, но отказались от неё по разным причинам. А затем завершите разговор вопросом о том, как она работает в нанимающей вас компании. У всех технологий есть свои недостатки, но понимание этого часто отсутствует, поэтому это отличный способ продемонстрировать свою зрелость и дать им возможность рассказать о своей системе. Большинство интервьюеров любят рассказывать о том, как работает их уникальная система.
Но есть кое-что, на что стоит обратить внимание, если вы кандидат с опытом! Будьте очень осторожны, приводя примеры десятилетней давности. И будьте столь же осторожны, чтобы не использовать примеры, основанные на неправильных технологиях. Если вы претендуете на должность, связанную с микросервисами, не говорите о разработке приложений WinForms больше, чем это абсолютно необходимо, даже если вы думаете, что это в тему.

8. Вопросы
Едва ли не самый популярный совет для собеседований. Не забудьте задать несколько вопросов. В конце концов, возможно, вы будете там работать, и стоит попытаться заранее обнаружить какие-либо тревожные сигналы! Или, если не получится, хотя бы воспользуйтесь возможностью, чтобы интервьюеры рассказали о чём-то, что им интересно — почти все любят описывать свою текущую архитектуру, структуру команды или повседневную работу! Просто помните, что задаваемые вами вопросы отражают вас: что вас интересует, что вас волнует?

Собеседование окончено!
Получили работу?
Поздравляю! Вас наняли. Это значит, что они считают, что вы справитесь. Поначалу всё может быть очень запутанным и пугающим, особенно если вы не знакомы с этой ролью или типом компании. Со временем всё наладится, приложите все усилия и держитесь, и через пару месяцев вы почувствуете себя как дома.

Не получили работу?
Какой бы ни была причина, не расстраивайтесь! Иногда это выгодно обеим сторонам: возможно, вы не подошли, а может быть, компания оказалась не той, о которой вы мечтали. А может быть, это просто невезение. Однажды я не смог написать простой SQL-запрос, хотя имею огромный опыт написания и оптимизации гораздо более сложных запросов и хранимых процедур. Иногда звёзды просто не сходятся.

Источник: https://www.viblo.se/posts/interviewing/
👍18
День 2406. #Книги
«.NET 8: приложения и сервисы. Практика создания проектов с использованием Blazor, .NET MAUI, gRPC, GraphQL.» 2-е изд. (Прайс М. — Астана: «Спринт Бук», 2025).

С днём знаний всех!

От издательства «Питер» мне пришла ещё одна книга на обзор.

750-страничный фолиант «обо всём». Просто по главам:
1. Обзор .NET.
2. SQL Server
3. EF Core
4. NoSQL
5. Конкурентность
6. Популярные NuGet-библиотеки
7. Дата и интернационализация
8. Минимальные API
9. Кэширование
10. Azure Functions
11. SignalR
12. GraphQL
13. gRPC
14. ASP.NET Core
15. Blazor
16. MAUI

По правде сказать, я всегда с подозрением относился к произведениям издательства <Packt> (они выпустили оригинал этой книги в 2023г.). Дело в том, что несколько лет назад я глянул пару их книг, которые выходили чуть ли не сразу после релиза какой-либо технологии, и те книги показались мне довольно поверхностными. А ещё часто они быстро переиздавали книгу, например, по очередной версии .NET, просто наспех обновляя информацию (и далеко не всегда корректно).

Но эта книга меня приятно удивила. Не обращайте внимания, что оригинал выпущен в 2023 году. В переводе (что редкость) встречаются вставки и комментарии с более актуальными данными (например, что VS 2022 с августа 2024 года не поддерживается для Mac).

Конечно, учитывая то, что книга сразу обо всём, глубокого погружения в темы от неё ждать не стоит. Но, мне кажется, она подойдёт для старших джунов/мидлов (людей, знакомых с языком и основами разработки приложений) как обзор технологий. Просто узнать, что есть в мире .NET, «что это и с чем его едят», чтобы, к примеру, встретив где-то название технологии, примерно понимать, о чём идет речь. Кроме того, в каждой главе есть ссылки на документацию и материалы для более подробного изучения. Также много внимания уделяется практике создания небольших приложений и утилит для демонстрации материала. Отдельная «фишка» - блиц-опрос в конце каждой главы для проверки, как вы усвоили материал (в конце книги есть ответы).

В общем, для знакомства с обширным миром .NET рекомендую.

PS
Это вторая книга из «трилогии» Марка Прайса.

Первая называется «C#12 and .NET 8. Modern Cross-Platform Development Fundamentals». Это «основы основ». Я не нашёл перевода книги на русский язык.

А третья книга также доступна в издательстве Питер «.NET 8: инструменты и навыки. Лучшие практики и паттерны проектирования, отладки и тестирования».

Ещё раз спасибо за подарок издательству «Питер». Присылайте что-нибудь ещё 😊
👍12
День 2407. #ЧтоНовенького
Самая Мощная Новинка .NET 10

Вам знакомо чувство, когда то, чем вы пользовались годами, всегда казалось… незаконченным? Например, методы-расширения. Они, конечно, крутые, но редко когда по-настоящему нужны. Это костыли, скрывающиеся за маской чистого синтаксиса. C# 14 исправляет это с помощью членов-расширений.

Методы расширения всегда были полусырыми. Какой-то статический метод, замаскированный под что-то более интересное. Ни свойств, ни операторов, никакой логической группировки связанных расширений, кроме неуклюжих статических классов. Они никогда не ощущались как принадлежащие к типу. Члены-расширения - взрослая версия того, чем всегда хотели быть методы расширения.

Пример 1: Переписывание ToObservable
Если вы использовали MVVM с WPF, Xamarin, MAUI и т.д., вы, вероятно, писали это сотню раз:
public static class ObservableExtensions
{
public static ObservableCollection<T>
ToObservable<T>(this IEnumerable<T> source)
=> new ObservableCollection<T>(source);
}

Это работает, но выглядит странно; это не похоже на расширение существующего типа. А вот то же с использованием члена-расширения:
public static class EnumerableExtensions
{
extension(IEnumerable<T> collection)
{
public ObservableCollection<T> ToObservable()
=> new ObservableCollection<T>(collection);
}
}

Видите разницу? Никакого беспорядка. Никаких странных «статических методов, притворяющихся реальными членами класса». Такое ощущение, что этим поведением владеет IEnumerable<T>.

Синтаксис
public static class NameOfExtensionClass
{
extension(YourType obj)
{
// Методы, свойства,
// операторы и вложенные типы
}
}


Пример 2: Добавляем свойства
Простой способ проверки на пустую коллекцию вместо !col.Any() или col.Count == 0 везде:
public static class CollectionExtensions
{
extension(ICollection<T> collection)
{
public bool IsEmpty => this.Count == 0;
}
}

Использование:
if (myList.IsEmpty)
{
// Наконец-то код читается, как надо
}

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

Пример 3: Статические Методы-Расширения
Вы можете добавлять статические члены к типам, которыми вы не владеете:
public static class DateTimeExtensions
{
extension(DateTime)
{
public static DateTime UnixEpoch
=> new DateTime(1970, 1, 1);
}
}


Как это работает изнутри
- Они не изменяют исходный тип.
- Компилятор и метаданные рассматривают их как «присоединённые» члены.
- Инструменты (например, IntelliSense) отображают их так, как будто они принадлежат типу.
- В основе всё по-прежнему безопасно и независимо, просто умнее.

Ограничения
- Никакой магии во время выполнения, они работают только во время компиляции.
- Нельзя добавлять поля или взаимодействовать с закрытыми членами.
- Поддержка инструментов вне современных IDE может немного отставать (пока).

Итого
Члены-расширения наконец-то делают расширения полноценными гражданами. Они позволяют API развиваться естественно. Они уменьшают беспорядок. Они делают код читаемым так, как будто он всегда был разработан таким образом. Это не просто синтаксический сахар, это корректировка курса. Они берут то, что начали методы расширения, и завершают работу. Более чистые API, легко обнаруживаемые функции, меньше служебных классов, скрывающихся в случайных пространствах имён, всё это просто кажется… правильным. Они не заставляют вас переосмысливать, как вы пишете часть кода. Они заставляют вас переосмыслить, как вы в целом проектируете его.

См. также «Используем Расширения C# 14 для Парсинга Enum»

Источник: https://blog.stackademic.com/net-10s-most-powerful-feature-isn-t-what-you-think-7d507dd254dc
👍31