День 2440. #Книги
«.NET 8: инструменты и навыки. Лучшие практики и паттерны проектирования, отладки и тестирования.» (Прайс М. — Астана: «Спринт Бук», 2025).
Ещё одну книгу издательство «Питер» прислали (точнее, передали на DotNext) мне на обзор.
В обзоре предыдущей книги я упоминал, что она вторая из серии книг Марка Прайса. Сегодня напишу про третью. В третей части 800 страниц, и, в отличие от второй, где сделан упор на обзор технологий, существующих в экосистеме .NET, в третей рассказывается о лучших практиках, которые можно применять как при создании приложений, так и для продвижения по карьерной лестнице. Вот некоторые темы:
- Эффективная работа с IDE;
- Работа с Git;
- Отладка;
- Ведение журналов, метрик и трассировок;
- Документирование кода и API;
- Рефлексия и динамическая загрузка сборок;
- Криптография;
- Создание ИИ-чатбота;
- Внедрение зависимостей;
- Тестирование: модульное, интеграционное, нагрузочное, функциональное;
- Контейнеризация;
- .NET Aspire;
- Паттерны и принципы проектирования;
- Основы архитектуры ПО;
- Командная работа и собеседования.
Как и в предыдущей книге, автор на практических примерах показывает наиболее интересные на его взгляд приёмы, которые используют многие .NET разработчики: горячие клавиши и функции IDE, команды Git, стратегии отладки, настройки телеметрии, тесты и т.п. Автор попытался охватить ВСЕ лучшие практики. Конечно, подробно в рамках одной книги это сделать нереально, поэтому ко всем перечисленным выше темам стоило бы добавить слово «Основы …». Но информации по каждой теме вполне достаточно, чтобы создать прочный базис, и автор не скупится на ссылки с дополнительной информацией. Кстати, плюсик в карму издательству: где это возможно, ссылки ведут на русскоязычные версии страниц документации.
Кроме того, как и в предыдущей книге, в конце каждой главы автор даёт несколько заданий для закрепления материала: ссылки на подробное изучение, практическое задание и вопросы для проверки знаний. Поэтому, если вы уверены, что знаете какую-то тему, можете начать с того, чтобы попытаться ответить на вопросы в конце главы. В конце книги есть ответы на все эти вопросы.
Очень интересная 19я глава про работу в команде и карьеру. В ней описаны хард- и софт-скилы, которых ждут от кандидатов, специализации, которые есть в современных вакансиях, роли в командах разработки. Далее довольно подробно описан процесс поиска работы, составления резюме и подготовки к собеседованию (ещё один плюсик издательству – добавили информацию об особенностях российского рынка разработки). Причём, описаны пути не только для новичков, но и для претендентов на старшие и руководящие должности. Описаны различные сценарии проведения собеседований, рассматриваются частые вопросы, которые задают кандидатам, в том числе поведенческие вопросы.
Интересный факт, одним из научных редакторов книги был «широко известный в узких кругах» Милан Йованович.
Ещё раз спасибо за подарок издательству «Питер». Присылайте что-нибудь ещё 😊
«.NET 8: инструменты и навыки. Лучшие практики и паттерны проектирования, отладки и тестирования.» (Прайс М. — Астана: «Спринт Бук», 2025).
Ещё одну книгу издательство «Питер» прислали (точнее, передали на DotNext) мне на обзор.
В обзоре предыдущей книги я упоминал, что она вторая из серии книг Марка Прайса. Сегодня напишу про третью. В третей части 800 страниц, и, в отличие от второй, где сделан упор на обзор технологий, существующих в экосистеме .NET, в третей рассказывается о лучших практиках, которые можно применять как при создании приложений, так и для продвижения по карьерной лестнице. Вот некоторые темы:
- Эффективная работа с IDE;
- Работа с Git;
- Отладка;
- Ведение журналов, метрик и трассировок;
- Документирование кода и API;
- Рефлексия и динамическая загрузка сборок;
- Криптография;
- Создание ИИ-чатбота;
- Внедрение зависимостей;
- Тестирование: модульное, интеграционное, нагрузочное, функциональное;
- Контейнеризация;
- .NET Aspire;
- Паттерны и принципы проектирования;
- Основы архитектуры ПО;
- Командная работа и собеседования.
Как и в предыдущей книге, автор на практических примерах показывает наиболее интересные на его взгляд приёмы, которые используют многие .NET разработчики: горячие клавиши и функции IDE, команды Git, стратегии отладки, настройки телеметрии, тесты и т.п. Автор попытался охватить ВСЕ лучшие практики. Конечно, подробно в рамках одной книги это сделать нереально, поэтому ко всем перечисленным выше темам стоило бы добавить слово «Основы …». Но информации по каждой теме вполне достаточно, чтобы создать прочный базис, и автор не скупится на ссылки с дополнительной информацией. Кстати, плюсик в карму издательству: где это возможно, ссылки ведут на русскоязычные версии страниц документации.
Кроме того, как и в предыдущей книге, в конце каждой главы автор даёт несколько заданий для закрепления материала: ссылки на подробное изучение, практическое задание и вопросы для проверки знаний. Поэтому, если вы уверены, что знаете какую-то тему, можете начать с того, чтобы попытаться ответить на вопросы в конце главы. В конце книги есть ответы на все эти вопросы.
Очень интересная 19я глава про работу в команде и карьеру. В ней описаны хард- и софт-скилы, которых ждут от кандидатов, специализации, которые есть в современных вакансиях, роли в командах разработки. Далее довольно подробно описан процесс поиска работы, составления резюме и подготовки к собеседованию (ещё один плюсик издательству – добавили информацию об особенностях российского рынка разработки). Причём, описаны пути не только для новичков, но и для претендентов на старшие и руководящие должности. Описаны различные сценарии проведения собеседований, рассматриваются частые вопросы, которые задают кандидатам, в том числе поведенческие вопросы.
Интересный факт, одним из научных редакторов книги был «широко известный в узких кругах» Милан Йованович.
Ещё раз спасибо за подарок издательству «Питер». Присылайте что-нибудь ещё 😊
👍25
День 2441. #ЗаметкиНаПолях
Используем Хранимые Процедуры в EF Core. Начало
Необходимость в хранимых процедурах в БД может возникнуть по разным причинам:
- сложный отчёт, объединяющий несколько таблиц с помощью агрегаций и оконных функций, когда LINQ-запрос генерирует неоптимальный SQL;
- нужно обновить таблицу, используя правильные блокировки, чтобы предотвратить состояние гонки.
В разных источниках об использовании хранимых процедур в EF Core можно встретить противоречивые советы: от «избегать использования чистого SQL любой ценой» до «полностью отказаться от EF и использовать ADO.NET». Ни то, ни другое не кажется правильным. EF Core отлично работает с функциями и процедурами БД.
В примерах далее будем использовать PostgreSQL, но те же принципы применимы и к другим реляционным БД.
Когда стоит использовать чистый SQL?
В большинстве случаев LINQ вполне подходит. EF Core преобразует ваш код C# в качественный SQL, а вы получаете типобезопасность и поддержку рефакторинга. Но есть исключения:
1. Нужна производительность, которую невозможно получить от LINQ. Сложные агрегации с несколькими соединениями, оконными функциями или запросы к отчётам часто выполняются быстрее, если написаны непосредственно на SQL. Вы можете протестировать и настроить запрос в инструменте работы с БД, прежде чем добавлять его в код.
2. Специфичные для БД функции. В PostgreSQL есть полнотекстовый поиск запросы к JSON и общие табличные выражения (CTE), которые не всегда имеют понятные эквиваленты в LINQ.
3. Есть существующая логика в БД (хранимые процедуры и функции, например, из унаследованной системы). Их прямой вызов лучше, чем переписывание всего на C#.
4. Нужны атомарные операции с корректной блокировкой. Хранимая процедура, которая координирует несколько обновлений с помощью блокировок FOR UPDATE, проще и безопаснее, чем пытаться управлять этим из кода приложения.
5. Нужно сократить количество запросов. Один вызов функции, агрегирующий данные из пяти таблиц, эффективнее пяти отдельных LINQ-запросов.
Рассмотрим, как это сделать.
1. Простая скалярная функция
Вот простая функция, которая показывает, сколько билетов осталось:
Ничего необычного, просто запрос, обёрнутый в функцию. Вызвать её в EF Core просто:
Обратите внимание на алиас
Синтаксис интерполированной строки (
Продолжение следует…
Источник: https://www.milanjovanovic.tech/blog/using-stored-procedures-and-functions-with-ef-core-and-postgresql
Используем Хранимые Процедуры в EF Core. Начало
Необходимость в хранимых процедурах в БД может возникнуть по разным причинам:
- сложный отчёт, объединяющий несколько таблиц с помощью агрегаций и оконных функций, когда LINQ-запрос генерирует неоптимальный SQL;
- нужно обновить таблицу, используя правильные блокировки, чтобы предотвратить состояние гонки.
В разных источниках об использовании хранимых процедур в EF Core можно встретить противоречивые советы: от «избегать использования чистого SQL любой ценой» до «полностью отказаться от EF и использовать ADO.NET». Ни то, ни другое не кажется правильным. EF Core отлично работает с функциями и процедурами БД.
В примерах далее будем использовать PostgreSQL, но те же принципы применимы и к другим реляционным БД.
Когда стоит использовать чистый SQL?
В большинстве случаев LINQ вполне подходит. EF Core преобразует ваш код C# в качественный SQL, а вы получаете типобезопасность и поддержку рефакторинга. Но есть исключения:
1. Нужна производительность, которую невозможно получить от LINQ. Сложные агрегации с несколькими соединениями, оконными функциями или запросы к отчётам часто выполняются быстрее, если написаны непосредственно на SQL. Вы можете протестировать и настроить запрос в инструменте работы с БД, прежде чем добавлять его в код.
2. Специфичные для БД функции. В PostgreSQL есть полнотекстовый поиск запросы к JSON и общие табличные выражения (CTE), которые не всегда имеют понятные эквиваленты в LINQ.
3. Есть существующая логика в БД (хранимые процедуры и функции, например, из унаследованной системы). Их прямой вызов лучше, чем переписывание всего на C#.
4. Нужны атомарные операции с корректной блокировкой. Хранимая процедура, которая координирует несколько обновлений с помощью блокировок FOR UPDATE, проще и безопаснее, чем пытаться управлять этим из кода приложения.
5. Нужно сократить количество запросов. Один вызов функции, агрегирующий данные из пяти таблиц, эффективнее пяти отдельных LINQ-запросов.
Рассмотрим, как это сделать.
1. Простая скалярная функция
Вот простая функция, которая показывает, сколько билетов осталось:
CREATE OR REPLACE FUNCTION
tt.tickets_left(ticket_type_id uuid)
RETURNS numeric LANGUAGE sql
AS $$
SELECT tt.available_quantity
FROM ticketing.ticket_types tt
WHERE tt.id = ticket_type_id
$$;
Ничего необычного, просто запрос, обёрнутый в функцию. Вызвать её в EF Core просто:
var result = await dbContext.Database.SqlQuery<int>(
$"""
SELECT tt.tickets_left({ticketTypeId}) AS "Value"
""")
.FirstAsync();
Обратите внимание на алиас
AS "Value"
. Когда EF Core сопоставляется с примитивным типом, он ожидает свойство с именем Value. Кавычки сохраняют точный регистр (PostgreSQL по умолчанию преобразует идентификаторы без кавычек в нижний регистр).Синтаксис интерполированной строки (
$"{ticketTypeId}"
) может показаться опасным и подверженным SQL-инъекциям, но EF Core автоматически преобразует его в параметризованный запрос. Это всего лишь удобный способ написания параметризованных запросов. Причина в том, что мы передаём методу SqlQuery не строку, а FormattableString. Это специальный тип, который сохраняет формат и аргументы отдельно, позволяя EF Core обрабатывать параметры.Продолжение следует…
Источник: https://www.milanjovanovic.tech/blog/using-stored-procedures-and-functions-with-ef-core-and-postgresql
👍16
День 2442. #ЗаметкиНаПолях
Используем Хранимые Процедуры в EF Core. Продолжение
Начало
2. Функция, возвращающая таблицу
Функции могут возвращать целые наборы результатов:
Эта функция объединяет заказы с их товарами, суммирует количество и возвращает несколько строк. Вы можете написать это на LINQ, но SQL понятнее, и вы можете протестировать его непосредственно в инструменте для работы с БД. Чтобы использовать её из C#, создайте DTO, соответствующий выходным данным функции:
Затем запросите функцию, как и любую другую таблицу:
Ключевым моментом является сопоставление имён столбцов со свойствами DTO с помощью алиасов. EF Core обрабатывает всё остальное автоматически.
Это простой случай без объединений, но вы можете использовать этот шаблон и в более сложных запросах. Однако вам придётся вручную проецировать данные в DTO, поскольку EF Core не может преобразовать объединения в чистом SQL в графы сущностей. Обычно вы в любом случае возвращаете плоскую структуру из функций, а затем при необходимости преобразуете её в более сложные модели на C#.
Понимание функций и процедур PostgreSQL
PostgreSQL различает функции и процедуры:
- Функции предназначены для возврата значений. Они могут возвращать скалярные значения, таблицы или даже сложные JSON-объекты. Они вызываются внутри транзакции с помощью SELECT и могут использоваться в запросах, предложениях WHERE, объединениях и других контекстах запросов.
- Процедуры предназначены для побочных эффектов. Они не возвращают значения напрямую, но могут изменять данные и иметь OUT-параметры. Вызываются с помощью CALL и идеально подходят для сложных операций, требующих явного управления транзакциями или выполнения нескольких связанных обновлений.
Используйте функции, когда нужно вернуть данные, используйте процедуры, когда нужно что-то изменить.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/using-stored-procedures-and-functions-with-ef-core-and-postgresql
Используем Хранимые Процедуры в EF Core. Продолжение
Начало
2. Функция, возвращающая таблицу
Функции могут возвращать целые наборы результатов:
CREATE OR REPLACE FUNCTION
tt.customer_order_summary(customer_id uuid)
RETURNS TABLE (
order_id uuid,
created_at timestamptz,
total numeric,
currency text,
qty numeric
)
LANGUAGE sql
AS $$
SELECT
o.id, o.created_at, o.total, o.currency,
COALESCE(SUM(oi.qty), 0) AS qty
FROM tt.orders o
LEFT JOIN tt.order_items oi ON oi.order_id = o.id
WHERE o.customer_id = customer_id
GROUP BY o.id, o.created_at, o.total, o.currency
ORDER BY o.created_at DESC
$$;
Эта функция объединяет заказы с их товарами, суммирует количество и возвращает несколько строк. Вы можете написать это на LINQ, но SQL понятнее, и вы можете протестировать его непосредственно в инструменте для работы с БД. Чтобы использовать её из C#, создайте DTO, соответствующий выходным данным функции:
public class OrderSummaryDto
{
public Guid OrderId { get; set; }
public DateTime CreatedAt { get; set; }
public decimal Total { get; set; }
public string Currency { get; set; }
public int Quantity { get; set; }
}
Затем запросите функцию, как и любую другую таблицу:
var orders =
await dbContext.Database
.SqlQuery<OrderSummaryDto>(
$"""
SELECT order_id AS OrderId, created_at AS CreatedAt, total AS Total, currency AS Currency, qty AS Quantity
FROM tt.customer_order_summary({customerId})
""")
.ToListAsync();
Ключевым моментом является сопоставление имён столбцов со свойствами DTO с помощью алиасов. EF Core обрабатывает всё остальное автоматически.
Это простой случай без объединений, но вы можете использовать этот шаблон и в более сложных запросах. Однако вам придётся вручную проецировать данные в DTO, поскольку EF Core не может преобразовать объединения в чистом SQL в графы сущностей. Обычно вы в любом случае возвращаете плоскую структуру из функций, а затем при необходимости преобразуете её в более сложные модели на C#.
Понимание функций и процедур PostgreSQL
PostgreSQL различает функции и процедуры:
- Функции предназначены для возврата значений. Они могут возвращать скалярные значения, таблицы или даже сложные JSON-объекты. Они вызываются внутри транзакции с помощью SELECT и могут использоваться в запросах, предложениях WHERE, объединениях и других контекстах запросов.
- Процедуры предназначены для побочных эффектов. Они не возвращают значения напрямую, но могут изменять данные и иметь OUT-параметры. Вызываются с помощью CALL и идеально подходят для сложных операций, требующих явного управления транзакциями или выполнения нескольких связанных обновлений.
Используйте функции, когда нужно вернуть данные, используйте процедуры, когда нужно что-то изменить.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/using-stored-procedures-and-functions-with-ef-core-and-postgresql
👍8
День 2443. #ЗаметкиНаПолях
Используем Хранимые Процедуры в EF Core. Окончание
Начало
Продолжение
3. Хранимая процедура с валидацией
Допустим, вам нужно изменить количество доступных билетов, но вы хотите предотвратить состояния гонки и проверить операцию:
Процедура делает несколько важных вещей:
- «Запирает» строку для обновления (FOR UPDATE), чтобы другая транзакция не могла её обновить, пока не завершится хранимая процедура;
- Проверяет бизнес-правила перед внесением изменений;
- Выводит понятные сообщения об ошибках при возникновении проблем;
- Сохраняет всё атомарно в одном запросе к БД.
Всё это можно было бы реализовать на C# с ручным управлением транзакциями и явной блокировкой, но это сложнее и подвержено ошибкам. Позвольте БД делать то, в чём она хороша. Вот как это можно вызвать из EF Core:
Процедура не возвращает значение, но, если она выбрасывает исключение (с помощью RAISE EXCEPTION), PostgreSQL передаст его в ваш код C#. Вы можете перехватить его и вернуть корректный ответ об ошибке.
Представления (view)
Представления БД подобны функциям без параметров. Это сохранённые запросы, к которым можно обращаться по имени. Можно выполнять запросы к ним, используя SqlQuery<T>, как к функциям:
Или можно сопоставлять их с типами сущностей в DbContext для полной поддержки LINQ. Представления отлично подходят для часто используемых запросов, не требующих параметров. Их также можно добавить в DbContext. Функции же обеспечивают гибкость параметризации.
Итого
EF Core не заставляет вас выбирать между LINQ и чистым SQL. Вы можете использовать и то, и другое. Используйте функции, когда нужно вернуть данные, процедуры, когда нужно изменить данные со сложной логикой, и чистые SQL-запросы, когда LINQ не может эффективно удовлетворить ваши требования. Сочетание удобства EF Core и мощности БД даёт гибкость в выборе подходящего инструмента для каждого сценария.
Источник: https://www.milanjovanovic.tech/blog/using-stored-procedures-and-functions-with-ef-core-and-postgresql
Используем Хранимые Процедуры в EF Core. Окончание
Начало
Продолжение
3. Хранимая процедура с валидацией
Допустим, вам нужно изменить количество доступных билетов, но вы хотите предотвратить состояния гонки и проверить операцию:
CREATE OR REPLACE PROCEDURE
tt.adjust_available_quantity(
ticket_type_id uuid,
delta numeric
)
LANGUAGE plpgsql AS $$
DECLARE
v_qty numeric;
v_avail numeric;
v_new_avail numeric;
BEGIN
SELECT quantity, available_quantity
INTO v_qty, v_avail
FROM tt.ticket_types
WHERE id = ticket_type_id
FOR UPDATE;
IF NOT FOUND THEN
RAISE EXCEPTION 'Тип билета % не найден', ticket_type_id;
END IF;
v_new_avail := v_avail + delta;
IF v_new_avail < 0 THEN
RAISE EXCEPTION 'Нельзя уменьшить < 0';
END IF;
IF v_new_avail > v_qty THEN
RAISE EXCEPTION 'Нельзя превышать общее количество';
END IF;
UPDATE tt.ticket_types
SET available = v_new_avail
WHERE id = ticket_type_id;
END;
$$;
Процедура делает несколько важных вещей:
- «Запирает» строку для обновления (FOR UPDATE), чтобы другая транзакция не могла её обновить, пока не завершится хранимая процедура;
- Проверяет бизнес-правила перед внесением изменений;
- Выводит понятные сообщения об ошибках при возникновении проблем;
- Сохраняет всё атомарно в одном запросе к БД.
Всё это можно было бы реализовать на C# с ручным управлением транзакциями и явной блокировкой, но это сложнее и подвержено ошибкам. Позвольте БД делать то, в чём она хороша. Вот как это можно вызвать из EF Core:
try
{
await dbContext.Database.ExecuteSqlAsync(
$"""
CALL tt.adjust_available_quantity({ticketTypeId},{quantity})
""");
}
catch (Exception e)
{
// обработка ошибки
}
Процедура не возвращает значение, но, если она выбрасывает исключение (с помощью RAISE EXCEPTION), PostgreSQL передаст его в ваш код C#. Вы можете перехватить его и вернуть корректный ответ об ошибке.
Представления (view)
Представления БД подобны функциям без параметров. Это сохранённые запросы, к которым можно обращаться по имени. Можно выполнять запросы к ним, используя SqlQuery<T>, как к функциям:
var results = await dbContext.Database
.SqlQuery<ActiveCustomerDto>(
$"SELECT * FROM tt.active_customers")
.ToListAsync();
Или можно сопоставлять их с типами сущностей в DbContext для полной поддержки LINQ. Представления отлично подходят для часто используемых запросов, не требующих параметров. Их также можно добавить в DbContext. Функции же обеспечивают гибкость параметризации.
Итого
EF Core не заставляет вас выбирать между LINQ и чистым SQL. Вы можете использовать и то, и другое. Используйте функции, когда нужно вернуть данные, процедуры, когда нужно изменить данные со сложной логикой, и чистые SQL-запросы, когда LINQ не может эффективно удовлетворить ваши требования. Сочетание удобства EF Core и мощности БД даёт гибкость в выборе подходящего инструмента для каждого сценария.
Источник: https://www.milanjovanovic.tech/blog/using-stored-procedures-and-functions-with-ef-core-and-postgresql
👍8
День 2444. #TipsAndTricks
Сеньорские Приёмы в C#
Некоторые функции C# настолько фундаментальны для чистой, современной разработки .NET, что их игнорирование выдаёт неопытность. Это не яркие новые игрушки, а практичные инструменты, которые мгновенно отличают код начинающего разработчика от отточенного кода опытного.
1. Раннее выявление ошибок внедрения зависимостей
Одна из самых серьёзных ошибок в приложениях .NET — это когда сервис успешно разрешается при запуске, но позже падает из-за недопустимых значений времени жизни или отсутствующих зависимостей. Этого можно избежать.
Код начинающего:
Вы не узнаете, что что-то неправильно настроено до того, как это не отвалится при обработке запроса.
Код опытного:
Эти две строки гарантируют обнаружение захвата зависимостей при запуске приложения, а не при его работе. ValidateScopes обнаруживает недопустимые времена жизни (например, внедрение scoped-сервиса в синглтон), а ValidateOnBuild заставляет контейнер попытаться создать сервисы при построении контейнера. Если что-то сломается, вы узнаете об этом ещё до запуска приложения.
2. Понимание и создание областей действия сервисов
Многие разработчики помещают все сервисы в Transient, не понимая, что это значит. Время жизни — это не просто шаблонный код; это модель памяти вашего приложения.
Код начинающего:
Ни контекста, ни объяснения, ни обдумывания, когда объект должен жить.
Ход мыслей опытного:
- Transient – новый экземпляр каждый раз,
- Scoped – один экземпляр на запрос/скоуп DI,
- Singleton – один экземпляр на всё время жизни приложения.
Создавая свою область действия, вы можете вручную изолировать контексты:
Опытный разработчик знает, когда ему нужна изоляция сервиса, например, для каждого пользователя, для каждого запроса или для каждого фонового задания.
3. Использование DateTimeOffset
Часовые пояса, летнее время и работа с UTC — вот где начинающие разработчики чаще всего терпят неудачу. Хуже всего то, что ваше приложение может работать… но потом перестаёт.
Ошибка начинающего:
Это сломается, как только код попадёт в другой часовой пояс (например, при размещении в облаке).
Подход опытного:
DateTimeOffset включает в себя как временную метку, так и её смещение. Это идеально подходит для реальных сценариев, таких как журналы, транзакции и планирование. Храните в формате UTC, отображайте по местному времени.
4. Использование алиасов пространств имён и типов
Загромождённые пространства имён и глубоко вложенные типы затрудняют чтение и рефакторинг кода. Тем не менее, начинающие разработчики редко используют алиасы:
Более чистый подход:
Алиасы — это не просто про сокращение имён; они проясняют суть. Они особенно полезны при рефакторинге или замене внешних библиотек, поскольку вы меняете всего одну строку — алиас.
Источник: https://blog.stackademic.com/5-c-features-that-instantly-expose-junior-devs-b126030c132e
Сеньорские Приёмы в C#
Некоторые функции C# настолько фундаментальны для чистой, современной разработки .NET, что их игнорирование выдаёт неопытность. Это не яркие новые игрушки, а практичные инструменты, которые мгновенно отличают код начинающего разработчика от отточенного кода опытного.
1. Раннее выявление ошибок внедрения зависимостей
Одна из самых серьёзных ошибок в приложениях .NET — это когда сервис успешно разрешается при запуске, но позже падает из-за недопустимых значений времени жизни или отсутствующих зависимостей. Этого можно избежать.
Код начинающего:
builder.Services.AddScoped<IMyService, MyService>();
// Нет настройки валидации
Вы не узнаете, что что-то неправильно настроено до того, как это не отвалится при обработке запроса.
Код опытного:
var builder = Program.CreateHostBuilder(args);
builder.Services.AddScoped<IMyService, MyService>();
builder.Host.UseDefaultServiceProvider(
(context, options) =>
{
options.ValidateOnBuild = true;
options.ValidateScopes = true;
});
Эти две строки гарантируют обнаружение захвата зависимостей при запуске приложения, а не при его работе. ValidateScopes обнаруживает недопустимые времена жизни (например, внедрение scoped-сервиса в синглтон), а ValidateOnBuild заставляет контейнер попытаться создать сервисы при построении контейнера. Если что-то сломается, вы узнаете об этом ещё до запуска приложения.
2. Понимание и создание областей действия сервисов
Многие разработчики помещают все сервисы в Transient, не понимая, что это значит. Время жизни — это не просто шаблонный код; это модель памяти вашего приложения.
Код начинающего:
builder.Services.AddTransient<UserSession>();
Ни контекста, ни объяснения, ни обдумывания, когда объект должен жить.
Ход мыслей опытного:
- Transient – новый экземпляр каждый раз,
- Scoped – один экземпляр на запрос/скоуп DI,
- Singleton – один экземпляр на всё время жизни приложения.
Создавая свою область действия, вы можете вручную изолировать контексты:
using var scope = serviceProvider.CreateScope();
var scopedService = scope
.ServiceProvider
.GetRequiredService<IScopedThing>();
Опытный разработчик знает, когда ему нужна изоляция сервиса, например, для каждого пользователя, для каждого запроса или для каждого фонового задания.
3. Использование DateTimeOffset
Часовые пояса, летнее время и работа с UTC — вот где начинающие разработчики чаще всего терпят неудачу. Хуже всего то, что ваше приложение может работать… но потом перестаёт.
Ошибка начинающего:
var orderTime = DateTime.Now; // локальное время
Это сломается, как только код попадёт в другой часовой пояс (например, при размещении в облаке).
Подход опытного:
var orderTime = DateTimeOffset.Now;
DateTimeOffset включает в себя как временную метку, так и её смещение. Это идеально подходит для реальных сценариев, таких как журналы, транзакции и планирование. Храните в формате UTC, отображайте по местному времени.
4. Использование алиасов пространств имён и типов
Загромождённые пространства имён и глубоко вложенные типы затрудняют чтение и рефакторинг кода. Тем не менее, начинающие разработчики редко используют алиасы:
System.Collections.Generic.Dictionary<System.Tuple<string, int>, List<MyNamespace.Models.ComplexThing>> myMap;
Более чистый подход:
using ComplexMap = System.Collections.Generic.Dictionary<(string, int), List<ComplexThing>>;
Алиасы — это не просто про сокращение имён; они проясняют суть. Они особенно полезны при рефакторинге или замене внешних библиотек, поскольку вы меняете всего одну строку — алиас.
Источник: https://blog.stackademic.com/5-c-features-that-instantly-expose-junior-devs-b126030c132e
👍25👎5
День 2445. #ВопросыНаСобеседовании
Давно не было на канале вопросов с собеседований. Смотрите все посты на эту тему по хэштэгу выше. Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы). Я решил разобрать их тут. Начнём с 4го, поскольку первые 3 приведены в его книге, обзор на которую я недавно выкладывал.
4. Интерфейсы и абстрактные классы
«Когда в .NET следует использовать интерфейс, а когда — абстрактный класс? Приведите примеры ситуаций, в которых один вариант будет более подходящим, чем другой».
Хороший ответ
«В .NET и интерфейсы, и абстрактные классы используются для определения контрактов в коде, но их применение различается в зависимости от необходимости наследования и типа полиморфизма.
Интерфейсы лучше всего использовать, когда нужно определить контракт для того, что делает класс, не навязывая ему, как именно это должно быть реализовано. Интерфейсы идеально подходят, когда требуется, чтобы несколько несвязанных классов реализовали один и тот же набор методов, гарантируя, что все реализующие классы будут придерживаться определённого поведения. Например, можно использовать интерфейс для определения стандартной структуры для различных типов платежей, где каждый тип платежа, такой как CreditCard, PayPal, Bitcoin, реализует методы обработки платежей, определённые интерфейсом.
Абстрактные классы используются, когда требуется общий код для нескольких тесно связанных классов. Абстрактный класс может предоставлять определённое поведение по умолчанию, а также определять абстрактное поведение, которое должно быть реализовано подклассами. Например, если вы создаёте приложение, моделирующее геометрические фигуры, можно использовать абстрактный класс для определения методов по умолчанию для вычисления площади и периметра, требуя при этом, чтобы подклассы, такие как Circle, Rectangle и Triangle, реализовали свои специфические вычисления.
Выбор между использованием интерфейса и абстрактного класса часто сводится к тому, нужно ли вам совместно использовать код (использовать абстрактный класс) или просто обеспечить общий интерфейс, не заботясь об иерархии (использовать интерфейс)».
Часто встречающийся неудачный ответ
«Я использую абстрактный класс всякий раз, когда мне нужно определить методы, которые не следует изменять, а интерфейсы — когда мне нужно просто реализовать множество различных методов. Поэтому, если у меня есть методы, которые не следует переопределять, я помещаю их в абстрактный класс».
Этот ответ демонстрирует непонимание фундаментального назначения и полезности абстрактных классов и интерфейсов:
- Непонимание абстрактных классов: Абстрактные классы предназначены не только для определения методов, которые «не следует изменять». Хотя абстрактный класс действительно может содержать конкретные методы, их основное предназначение — служить базовым классом для расширения другими классами, предоставляя общий код и определяя абстрактные методы, которые должны быть реализованы подклассами. Тот факт, что они могут включать непереопределяемые методы (с помощью ключевого слова sealed в C#), является второстепенным по сравнению с их основной функцией.
- Непонимание интерфейсов: Утверждение, что интерфейсы используются «только для реализации набора различных методов», слишком упрощает их роль. Интерфейсы определяют контракт, которому следуют классы, что критически важно для разработки систем, в которых различные классы могут использоваться взаимозаменяемо, не зная их конкретных реализаций.
Неточность здесь проистекает из непонимания полиморфизма, повторного использования кода и замысла проектирования интерфейсов и абстрактных классов, что является фундаментальным аспектом объектно-ориентированного проектирования.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Давно не было на канале вопросов с собеседований. Смотрите все посты на эту тему по хэштэгу выше. Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы). Я решил разобрать их тут. Начнём с 4го, поскольку первые 3 приведены в его книге, обзор на которую я недавно выкладывал.
4. Интерфейсы и абстрактные классы
«Когда в .NET следует использовать интерфейс, а когда — абстрактный класс? Приведите примеры ситуаций, в которых один вариант будет более подходящим, чем другой».
Хороший ответ
«В .NET и интерфейсы, и абстрактные классы используются для определения контрактов в коде, но их применение различается в зависимости от необходимости наследования и типа полиморфизма.
Интерфейсы лучше всего использовать, когда нужно определить контракт для того, что делает класс, не навязывая ему, как именно это должно быть реализовано. Интерфейсы идеально подходят, когда требуется, чтобы несколько несвязанных классов реализовали один и тот же набор методов, гарантируя, что все реализующие классы будут придерживаться определённого поведения. Например, можно использовать интерфейс для определения стандартной структуры для различных типов платежей, где каждый тип платежа, такой как CreditCard, PayPal, Bitcoin, реализует методы обработки платежей, определённые интерфейсом.
Абстрактные классы используются, когда требуется общий код для нескольких тесно связанных классов. Абстрактный класс может предоставлять определённое поведение по умолчанию, а также определять абстрактное поведение, которое должно быть реализовано подклассами. Например, если вы создаёте приложение, моделирующее геометрические фигуры, можно использовать абстрактный класс для определения методов по умолчанию для вычисления площади и периметра, требуя при этом, чтобы подклассы, такие как Circle, Rectangle и Triangle, реализовали свои специфические вычисления.
Выбор между использованием интерфейса и абстрактного класса часто сводится к тому, нужно ли вам совместно использовать код (использовать абстрактный класс) или просто обеспечить общий интерфейс, не заботясь об иерархии (использовать интерфейс)».
Часто встречающийся неудачный ответ
«Я использую абстрактный класс всякий раз, когда мне нужно определить методы, которые не следует изменять, а интерфейсы — когда мне нужно просто реализовать множество различных методов. Поэтому, если у меня есть методы, которые не следует переопределять, я помещаю их в абстрактный класс».
Этот ответ демонстрирует непонимание фундаментального назначения и полезности абстрактных классов и интерфейсов:
- Непонимание абстрактных классов: Абстрактные классы предназначены не только для определения методов, которые «не следует изменять». Хотя абстрактный класс действительно может содержать конкретные методы, их основное предназначение — служить базовым классом для расширения другими классами, предоставляя общий код и определяя абстрактные методы, которые должны быть реализованы подклассами. Тот факт, что они могут включать непереопределяемые методы (с помощью ключевого слова sealed в C#), является второстепенным по сравнению с их основной функцией.
- Непонимание интерфейсов: Утверждение, что интерфейсы используются «только для реализации набора различных методов», слишком упрощает их роль. Интерфейсы определяют контракт, которому следуют классы, что критически важно для разработки систем, в которых различные классы могут использоваться взаимозаменяемо, не зная их конкретных реализаций.
Неточность здесь проистекает из непонимания полиморфизма, повторного использования кода и замысла проектирования интерфейсов и абстрактных классов, что является фундаментальным аспектом объектно-ориентированного проектирования.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👍22👎2
День 2446. #Карьера
Не Только Код: Что Делает Тебя Сеньором. Начало
Вы замечали, что на собеседованиях на старшие должности вас всё меньше гоняют по техническим вопросам? Вместо этого спрашивают что-то вроде: «Что такое технический долг?» Или «Что вы делаете, когда сроки срываются? Как расставляете приоритеты в задачах?» Быть сеньором не значит освоить каждый протокол или запомнить каждый алгоритм. Это рассудительность, работа с людьми, принятие решений, долгосрочное мышление. За годы опыта вы совершаете ошибки, и хорошо, если извлекаете из них уроки. Рассмотрим некоторые наиболее важные уроки на пути к сеньорской должности. Будем использовать вымышленного персонажа Эдди.
1. Рассуждения важнее правил
Однажды нам нужно было добавить новые поля в таблицу DynamoDB в рабочей среде. Эдди, блестящий джун, предложил тяжёлую миграцию: реплицировать таблицу, синхронизировать её с потоками, а затем, после недели тестирования, выполнить переход. План казался исчерпывающим, но он не рассматривал более простые варианты. Почему не обновить существующие записи с помощью скрипта? «Это опасно. Нельзя обновлять данные БД в проде», - воскликнул Эдди, без каких-либо обоснований или данных. Но в этой системе БД всего лишь обеспечивала работу офлайн-заданий, поэтому риск был невелик.
Подход Эдди отражал распространённую ловушку: воспринимать «лучшие практики» как абсолютную истину. «Никогда не трогайте прод». «Микросервисы всегда лучше монолита». Правила кажутся безопасными, но они зависят от контекста. Опытный разработчик - не тот, у которого в арсенале обширный набор правил. Он всегда старается объяснить, почему решение имеет смысл в данной ситуации, и взвешивает риски и компромиссы.
2. Не делайте предположений, проверяйте
Когда системы выходят из строя, легко сделать поспешные выводы: «Должно быть, дело в БД», «Возможно, это новое развёртывание». Но предположения отнимают драгоценные часы, а когда прод лежит, каждая минута на счету. Лучшие инженеры не полагаются на догадки. Они доверяют данным и используют свой опыт, чтобы определить, где искать в первую очередь, но всегда проверяют. Настоящее мастерство не в «правильной догадке», а в дисциплине, позволяющей проверять логи, воспроизводить проблемы и просить коллег проверить работоспособность. Проверка решает проблемы быстрее и укрепляет доверие. Ничто не подрывает доверие быстрее, чем самоуверенное обвинение в чём-то неправильном.
3. Сомневайтесь в хороших новостях
Однажды Эдди на радостях воскликнул: «API работает на 20% быстрее после моих изменений!». Странно, его просили просто отрефакторить код. Это выглядело как победа, но, когда что-то выглядит слишком хорошо, чтобы быть правдой, так оно и есть. После анализа мы выяснили: он случайно удалил логику повторных попыток в наших нисходящих вызовах. За блестящими цифрами копились скрытые сбои.
Всегда относитесь к внезапным чудесам скептически. Когда показатели внезапно улучшаются, первым вопросом должен быть: «Что мы сломали?» Здоровый скептицизм не даёт праздновать ложные победы. Он подкрепляет истину: данные ценны, только когда вы понимаете, почему они возникли.
4. Механизм важнее благих намерений
«Мы просто не забудем сделать X» - это всегда исходит из благих намерений, но они не защищают системы. Люди отвлекаются, устают или поджимают сроки. Однажды критический инцидент произошёл из-за того, что кто-то забыл запустить скрипт после развёртывания. Дело не в невнимательности, а в хрупкости процесса. Самые сильные команды не полагаются на память или обещания. Автоматизированные проверки, конвейеры CI/CD, флаги функций, ревью кода — это не «приятные мелочи», а защитные барьеры. Вместо того, чтобы доверять кому-то ручной запуск скрипта, вы делаете скрипт частью процесса развёртывания.
Ведущие инженеры проектируют системы, где безопасный путь — это также и самый простой. Ошибки неизбежны. Сильную инженерную культуру определяет способность системы выявлять эти ошибки раньше, чем это сделают ваши клиенты.
Окончание следует…
Источник: https://levelup.gitconnected.com/beyond-the-code-lessons-that-make-you-senior-1ba44469aa42
Не Только Код: Что Делает Тебя Сеньором. Начало
Вы замечали, что на собеседованиях на старшие должности вас всё меньше гоняют по техническим вопросам? Вместо этого спрашивают что-то вроде: «Что такое технический долг?» Или «Что вы делаете, когда сроки срываются? Как расставляете приоритеты в задачах?» Быть сеньором не значит освоить каждый протокол или запомнить каждый алгоритм. Это рассудительность, работа с людьми, принятие решений, долгосрочное мышление. За годы опыта вы совершаете ошибки, и хорошо, если извлекаете из них уроки. Рассмотрим некоторые наиболее важные уроки на пути к сеньорской должности. Будем использовать вымышленного персонажа Эдди.
1. Рассуждения важнее правил
Однажды нам нужно было добавить новые поля в таблицу DynamoDB в рабочей среде. Эдди, блестящий джун, предложил тяжёлую миграцию: реплицировать таблицу, синхронизировать её с потоками, а затем, после недели тестирования, выполнить переход. План казался исчерпывающим, но он не рассматривал более простые варианты. Почему не обновить существующие записи с помощью скрипта? «Это опасно. Нельзя обновлять данные БД в проде», - воскликнул Эдди, без каких-либо обоснований или данных. Но в этой системе БД всего лишь обеспечивала работу офлайн-заданий, поэтому риск был невелик.
Подход Эдди отражал распространённую ловушку: воспринимать «лучшие практики» как абсолютную истину. «Никогда не трогайте прод». «Микросервисы всегда лучше монолита». Правила кажутся безопасными, но они зависят от контекста. Опытный разработчик - не тот, у которого в арсенале обширный набор правил. Он всегда старается объяснить, почему решение имеет смысл в данной ситуации, и взвешивает риски и компромиссы.
2. Не делайте предположений, проверяйте
Когда системы выходят из строя, легко сделать поспешные выводы: «Должно быть, дело в БД», «Возможно, это новое развёртывание». Но предположения отнимают драгоценные часы, а когда прод лежит, каждая минута на счету. Лучшие инженеры не полагаются на догадки. Они доверяют данным и используют свой опыт, чтобы определить, где искать в первую очередь, но всегда проверяют. Настоящее мастерство не в «правильной догадке», а в дисциплине, позволяющей проверять логи, воспроизводить проблемы и просить коллег проверить работоспособность. Проверка решает проблемы быстрее и укрепляет доверие. Ничто не подрывает доверие быстрее, чем самоуверенное обвинение в чём-то неправильном.
3. Сомневайтесь в хороших новостях
Однажды Эдди на радостях воскликнул: «API работает на 20% быстрее после моих изменений!». Странно, его просили просто отрефакторить код. Это выглядело как победа, но, когда что-то выглядит слишком хорошо, чтобы быть правдой, так оно и есть. После анализа мы выяснили: он случайно удалил логику повторных попыток в наших нисходящих вызовах. За блестящими цифрами копились скрытые сбои.
Всегда относитесь к внезапным чудесам скептически. Когда показатели внезапно улучшаются, первым вопросом должен быть: «Что мы сломали?» Здоровый скептицизм не даёт праздновать ложные победы. Он подкрепляет истину: данные ценны, только когда вы понимаете, почему они возникли.
4. Механизм важнее благих намерений
«Мы просто не забудем сделать X» - это всегда исходит из благих намерений, но они не защищают системы. Люди отвлекаются, устают или поджимают сроки. Однажды критический инцидент произошёл из-за того, что кто-то забыл запустить скрипт после развёртывания. Дело не в невнимательности, а в хрупкости процесса. Самые сильные команды не полагаются на память или обещания. Автоматизированные проверки, конвейеры CI/CD, флаги функций, ревью кода — это не «приятные мелочи», а защитные барьеры. Вместо того, чтобы доверять кому-то ручной запуск скрипта, вы делаете скрипт частью процесса развёртывания.
Ведущие инженеры проектируют системы, где безопасный путь — это также и самый простой. Ошибки неизбежны. Сильную инженерную культуру определяет способность системы выявлять эти ошибки раньше, чем это сделают ваши клиенты.
Окончание следует…
Источник: https://levelup.gitconnected.com/beyond-the-code-lessons-that-make-you-senior-1ba44469aa42
👍28
День 2447. #Карьера
Не Только Код: Что Делает Тебя Сеньором. Окончание
Начало
5. Дисциплина отказа
Однажды Эдди был на встрече с одним из клиентов. Они использовали наши API и хотели внедрить новую логику фильтрации. Вместо того, чтобы реализовывать её на своей стороне, они предложили сделать это на нашем бэкенде. Они убедили Эдди согласиться. Но когда он поделился решением с командой, все ведущие инженеры выразили несогласие. У клиентов не было веских причин так делать, они просто хотели переложить всю сложность на нас.
Это один из самых сложных уроков в карьере. Сеньоры не из тех, кто на всё отвечает «да». Они защищают свои команды от ненужной сложности, отвергают бессмысленные компромиссы, и понимают, что не каждый запрос заслуживает одобрения.
6. Рост начинается с ответственности
В начале карьеры мы во многом полагаемся на старших коллег. Но инженерия полна неопределённости. Старшие коллеги — не те, кто всегда знает правильный ответ; они принимают обоснованные решения, взвешивают компромиссы и берут на себя ответственность. Когда эти решения оказываются неправильными, они не перекладывают вину на других, а принимают последствия, учатся и корректируют свои действия.
7. От защитника к наставнику
Естественным инстинктом является оградить младших коллег от неудач. Оставлять длинные комментарии, объяснять каждую деталь или даже исправлять их код. Это может предотвратить сиюминутные трудности, но также лишает их возможности извлекать уроки, которые можно извлечь только из личного опыта.
Роль сеньора не в предотвращении ошибок, а в создании безопасного пространства, где ошибки могут происходить без катастрофических последствий. Настоящий рост достигается благодаря ошибкам, восстановлению и дальнейшему развитию. Высший показатель лидерства — способность команды процветать без вас. Если вы создали культуру, в которой люди учатся на безопасных ошибках, то вы выполнили свою задачу наставника.
8. Простота масштабируется, сложность ломается
Сделать просто почти всегда лучше, чем заумно. Иногда возникает соблазн проектировать систему с учётом всех возможных сценариев, «закладывая фундамент» в будущее с помощью абстракций и расширяемости. Но чаще всего эти дополнительные уровни превращаются в мёртвый груз. Старайтесь избегать преждевременной сложности любой ценой. Простые решения не только проще писать, но и легче тестировать, поддерживать и адаптировать к ним новых разработчиков.
9. Каждая система рано или поздно ломается, будьте готовы
Ни одна система не является по-настоящему отказоустойчивой. Код живой: он развивается, интегрируется с новыми зависимостями и адаптируется к меняющимся требованиям. Каждое изменение, каким бы небольшим и хорошо протестированным оно ни было, увеличивает вероятность возникновения сбоев. Сеньоры предвидят это, чётко соблюдая гарантии, тщательно контролируя версии API и настраивая мониторинг и т.п. Тесты ценны, но не являются панацеей. При достаточном времени, достаточном трафике и достаточном количестве изменений любая система выйдет из строя. Вопрос - когда и насколько вы готовы к этому.
10. Принимайте изменения и адаптируйтесь
Изменения постоянны, и их темп только ускоряется. Сейчас LLM уже стали частью повседневных рабочих процессов. Есть ли вокруг них хайп? Да. Но было бы ошибкой предполагать, что они просто исчезнут, а мы станем работать по старинке. Они продолжат совершенствоваться и постепенно брать на себя всё больше ответственности.
Мы ещё в начале пути к пониманию долгосрочного влияния LLM. Но уже понятно, что они не заменят инженерное суждение, а при разумном использовании могут ускорить процесс.
Поэтому принятие изменений не означает слепого доверия к коду, сгенерированному ИИ, и не предполагает, что он решит все проблемы. Это означает быть в курсе событий, осторожно экспериментировать и интегрировать то, что действительно приносит пользу, учитывая риски.
Источник: https://levelup.gitconnected.com/beyond-the-code-lessons-that-make-you-senior-1ba44469aa42
Не Только Код: Что Делает Тебя Сеньором. Окончание
Начало
5. Дисциплина отказа
Однажды Эдди был на встрече с одним из клиентов. Они использовали наши API и хотели внедрить новую логику фильтрации. Вместо того, чтобы реализовывать её на своей стороне, они предложили сделать это на нашем бэкенде. Они убедили Эдди согласиться. Но когда он поделился решением с командой, все ведущие инженеры выразили несогласие. У клиентов не было веских причин так делать, они просто хотели переложить всю сложность на нас.
Это один из самых сложных уроков в карьере. Сеньоры не из тех, кто на всё отвечает «да». Они защищают свои команды от ненужной сложности, отвергают бессмысленные компромиссы, и понимают, что не каждый запрос заслуживает одобрения.
6. Рост начинается с ответственности
В начале карьеры мы во многом полагаемся на старших коллег. Но инженерия полна неопределённости. Старшие коллеги — не те, кто всегда знает правильный ответ; они принимают обоснованные решения, взвешивают компромиссы и берут на себя ответственность. Когда эти решения оказываются неправильными, они не перекладывают вину на других, а принимают последствия, учатся и корректируют свои действия.
7. От защитника к наставнику
Естественным инстинктом является оградить младших коллег от неудач. Оставлять длинные комментарии, объяснять каждую деталь или даже исправлять их код. Это может предотвратить сиюминутные трудности, но также лишает их возможности извлекать уроки, которые можно извлечь только из личного опыта.
Роль сеньора не в предотвращении ошибок, а в создании безопасного пространства, где ошибки могут происходить без катастрофических последствий. Настоящий рост достигается благодаря ошибкам, восстановлению и дальнейшему развитию. Высший показатель лидерства — способность команды процветать без вас. Если вы создали культуру, в которой люди учатся на безопасных ошибках, то вы выполнили свою задачу наставника.
8. Простота масштабируется, сложность ломается
Сделать просто почти всегда лучше, чем заумно. Иногда возникает соблазн проектировать систему с учётом всех возможных сценариев, «закладывая фундамент» в будущее с помощью абстракций и расширяемости. Но чаще всего эти дополнительные уровни превращаются в мёртвый груз. Старайтесь избегать преждевременной сложности любой ценой. Простые решения не только проще писать, но и легче тестировать, поддерживать и адаптировать к ним новых разработчиков.
9. Каждая система рано или поздно ломается, будьте готовы
Ни одна система не является по-настоящему отказоустойчивой. Код живой: он развивается, интегрируется с новыми зависимостями и адаптируется к меняющимся требованиям. Каждое изменение, каким бы небольшим и хорошо протестированным оно ни было, увеличивает вероятность возникновения сбоев. Сеньоры предвидят это, чётко соблюдая гарантии, тщательно контролируя версии API и настраивая мониторинг и т.п. Тесты ценны, но не являются панацеей. При достаточном времени, достаточном трафике и достаточном количестве изменений любая система выйдет из строя. Вопрос - когда и насколько вы готовы к этому.
10. Принимайте изменения и адаптируйтесь
Изменения постоянны, и их темп только ускоряется. Сейчас LLM уже стали частью повседневных рабочих процессов. Есть ли вокруг них хайп? Да. Но было бы ошибкой предполагать, что они просто исчезнут, а мы станем работать по старинке. Они продолжат совершенствоваться и постепенно брать на себя всё больше ответственности.
Мы ещё в начале пути к пониманию долгосрочного влияния LLM. Но уже понятно, что они не заменят инженерное суждение, а при разумном использовании могут ускорить процесс.
Поэтому принятие изменений не означает слепого доверия к коду, сгенерированному ИИ, и не предполагает, что он решит все проблемы. Это означает быть в курсе событий, осторожно экспериментировать и интегрировать то, что действительно приносит пользу, учитывая риски.
Источник: https://levelup.gitconnected.com/beyond-the-code-lessons-that-make-you-senior-1ba44469aa42
👍29
День 2448. #ЗаметкиНаПолях
Использование Токенов Отмены
Сегодня посмотрим, почему стоит использовать токены отмены в вашем API. Дело не только в вашем коде.
Представьте, что у нас есть длительный SQL-запрос, например:
Конечно, это всего лишь «бесполезный» демонстрационный код, но идея в том, что он выполняется долго и может значительно нагружать процессор, память и ввод-вывод. Если это часть вашего (REST) API и HTTP-запрос отменяется, SQL-запрос всё равно продолжит выполняться.
Вы можете легко проверить это с помощью:
И вы увидите, что этот запрос выполняется, даже если вызов REST прерывается. При завершении консольного приложения этого не произойдёт, т.к. это приведет к прерыванию соединения с БД и транзакции.
Теперь, если мы предоставим токен отмены:
Теперь, если запустить тот же запрос к sys.dm_exec_requests, вы увидите, что после отмены, сервер также прерывает и SQL-запрос.
Так ваш код не только лучше масштабируется, поскольку может освобождать ненужные ресурсы, но и, если внешняя система также поддерживает это, она также может экономить ресурсы.
В этом примере это был SQL-сервер, который реагировал на отмену. Но если вы также передадите токен отмены в HttpClient, это прервёт запрос, и другая сторона может отреагировать таким же образом. См. Отмена Операции при Отмене HTTP Запроса.
Не факт, что это поддерживают все технологии. Похоже, что, например, провайдер для Postgresql это поддерживает. Так что технически это возможно, но опыт может отличаться. В любом случае, добавление токена — хорошая практика, поскольку поставщики могут улучшить поддержку в будущем.
Всегда ли нужно предоставлять токен отмены?
Если вы видите в вопросе слова «всегда» или «никогда», ответ, скорее всего, «нет». Конечно, общее правило — добавлять токен отмены к вызовам, которые его поддерживают, но давайте посмотрим на следующий код:
Представьте, что пользователь отменяет запрос, во время сохранения адреса. Пользователь добавляется, а адрес и роль — нет. Теперь вы находитесь в несогласованном состоянии. Конечно, этот пример надуманный. Простое решение — вызвать SaveChangesAsync только один раз в конце или использовать транзакцию, которая фиксируется в конце один раз. Но иногда у вас есть сторонний код, который также хранит информацию где-то, где вы не можете её контролировать. Поэтому нужно убедиться, что вы не столкнётесь с несогласованными состояниями из-за того, что кто-то прервал запрос!
Источник: https://steven-giesel.com/blogPost/080baaef-27d4-4d98-b0a8-9c3ab96c335e/use-cancellationtokens
Использование Токенов Отмены
Сегодня посмотрим, почему стоит использовать токены отмены в вашем API. Дело не только в вашем коде.
Представьте, что у нас есть длительный SQL-запрос, например:
-- MSSQL
SELECT COUNT_BIG(*)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
CROSS JOIN sys.all_objects c
CROSS JOIN sys.all_objects d
CROSS JOIN sys.all_objects e;
Конечно, это всего лишь «бесполезный» демонстрационный код, но идея в том, что он выполняется долго и может значительно нагружать процессор, память и ввод-вывод. Если это часть вашего (REST) API и HTTP-запрос отменяется, SQL-запрос всё равно продолжит выполняться.
Вы можете легко проверить это с помощью:
SELECT * FROM sys.dm_exec_requests WHERE status = 'running';
И вы увидите, что этот запрос выполняется, даже если вызов REST прерывается. При завершении консольного приложения этого не произойдёт, т.к. это приведет к прерыванию соединения с БД и транзакции.
Теперь, если мы предоставим токен отмены:
using var cts =
new CancellationTokenSource(TimeSpan.FromSeconds(2));
try
{
await dbContext.Database.ExecuteSqlAsync(
$"""
SELECT COUNT_BIG(*)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
CROSS JOIN sys.all_objects c
CROSS JOIN sys.all_objects d
CROSS JOIN sys.all_objects e;
""", cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Запрос отменён!");
}
Теперь, если запустить тот же запрос к sys.dm_exec_requests, вы увидите, что после отмены, сервер также прерывает и SQL-запрос.
Так ваш код не только лучше масштабируется, поскольку может освобождать ненужные ресурсы, но и, если внешняя система также поддерживает это, она также может экономить ресурсы.
В этом примере это был SQL-сервер, который реагировал на отмену. Но если вы также передадите токен отмены в HttpClient, это прервёт запрос, и другая сторона может отреагировать таким же образом. См. Отмена Операции при Отмене HTTP Запроса.
Не факт, что это поддерживают все технологии. Похоже, что, например, провайдер для Postgresql это поддерживает. Так что технически это возможно, но опыт может отличаться. В любом случае, добавление токена — хорошая практика, поскольку поставщики могут улучшить поддержку в будущем.
Всегда ли нужно предоставлять токен отмены?
Если вы видите в вопросе слова «всегда» или «никогда», ответ, скорее всего, «нет». Конечно, общее правило — добавлять токен отмены к вызовам, которые его поддерживают, но давайте посмотрим на следующий код:
var user = new User { Name = dto.Name };
db.Users.Add(user);
await _db.SaveChangesAsync(ct);
var address =
new Address { UserId = user.Id, City = dto.City };
db.Addresses.Add(address);
await _db.SaveChangesAsync(ct);
var role =
new Role { UserId = user.Id, Name = "Member" };
db.Roles.Add(role);
await _db.SaveChangesAsync(ct);
Представьте, что пользователь отменяет запрос, во время сохранения адреса. Пользователь добавляется, а адрес и роль — нет. Теперь вы находитесь в несогласованном состоянии. Конечно, этот пример надуманный. Простое решение — вызвать SaveChangesAsync только один раз в конце или использовать транзакцию, которая фиксируется в конце один раз. Но иногда у вас есть сторонний код, который также хранит информацию где-то, где вы не можете её контролировать. Поэтому нужно убедиться, что вы не столкнётесь с несогласованными состояниями из-за того, что кто-то прервал запрос!
Источник: https://steven-giesel.com/blogPost/080baaef-27d4-4d98-b0a8-9c3ab96c335e/use-cancellationtokens
👍18
День 2449. #TipsAndTricks #Blazor
Лучшие Практики по Созданию Веб-Приложений в Blazor. Начало
Рассмотрим 9 рекомендаций по созданию веб-приложений Blazor.
1. Понимание жизненного цикла компонента
Первый и самый важный шаг при изучении Blazor — это правильное понимание жизненного цикла компонента. Blazor использует компонентно-ориентированную систему рендеринга, похожую на другие современные фреймворки веб-приложений, такие как Angular или React. См. подробнее о создании Blazor-компонентов.
Помимо изучения реализации компонентов Blazor, важно понимать, когда компонент Blazor автоматически перерисовывается и как управлять этим поведением. Например, мы можем переопределить метод жизненного цикла ShouldRender для управления обновлением UI. Если метод возвращает true, компонент перерисовывается.
2. Выбор правильного размера компонента
Одна из самых сложных задач - решение о том, когда следует разбить код на несколько компонентов. Нужно начать с простого. Сначала создаём маршрутизируемый компонент страницы и пишем весь код в нём. В какой-то момент код разрастается, и приходит понимание, что часть кода не связана с другой частью. Например, обработка формы никак не связана с отображением таблицы. Это сигнал, чтобы разделить компоненты, извлечь дочерние компоненты и превратить компонент страницы в оркестратор нескольких дочерних компонентов.
Не существует правильного или неправильного подхода, и требуется опыт, чтобы понять, что работает лучше всего. Одно из полезных правил - выделять то, что связано друг с другом, и ценить связность больше, чем размер (количество строк кода).
3. Реализация независимого режима рендеринга для Blazor-компонентов
С появлением интерактивного режима рендеринга в .NET 8 мы получаем гораздо больше гибкости по сравнению с предыдущими версиями Blazor. Например, мы можем реализовать веб-приложение, полностью отрисовываемое на сервере, без какой-либо интерактивности. Или можем комбинировать интерактивность Blazor Server и Blazor WebAssembly в одном приложении.
Для обеспечения гибкой архитектуры рекомендуется настроить компоненты Blazor так, чтобы они не зависели от режима рендеринга. Т.е. не задавать тип интерактивности внутри каждого компонента, а задавать его только на верхнем уровне. Это позволяет использовать компонент как часть интерактивного приложения Blazor Server и Blazor WebAssembly.
4. Изучите правильную обработку событий
Узнайте, как привязывать методы C# к событиям, вызываемым HTML-элементами. Это фундаментальный механизм для реализации обработчиков onClick для кнопок или обработчиков отправки для HTML-форм. При регистрации событий .NET, таких как событие LocationChanged класса NavigationManager, обязательно отписывайтесь от события при удалении компонента. В противном случае компонент не будет уничтожен сборщиком мусора.
Что касается связи между компонентами, обратные вызовы событий (EventCallback) являются стандартным способом осуществления обратного вызова родительского компонента из дочернего компонента.
Окончание следует…
Источник: https://www.telerik.com/blogs/blazor-basics-9-best-practices-building-blazor-web-applications
Лучшие Практики по Созданию Веб-Приложений в Blazor. Начало
Рассмотрим 9 рекомендаций по созданию веб-приложений Blazor.
1. Понимание жизненного цикла компонента
Первый и самый важный шаг при изучении Blazor — это правильное понимание жизненного цикла компонента. Blazor использует компонентно-ориентированную систему рендеринга, похожую на другие современные фреймворки веб-приложений, такие как Angular или React. См. подробнее о создании Blazor-компонентов.
Помимо изучения реализации компонентов Blazor, важно понимать, когда компонент Blazor автоматически перерисовывается и как управлять этим поведением. Например, мы можем переопределить метод жизненного цикла ShouldRender для управления обновлением UI. Если метод возвращает true, компонент перерисовывается.
2. Выбор правильного размера компонента
Одна из самых сложных задач - решение о том, когда следует разбить код на несколько компонентов. Нужно начать с простого. Сначала создаём маршрутизируемый компонент страницы и пишем весь код в нём. В какой-то момент код разрастается, и приходит понимание, что часть кода не связана с другой частью. Например, обработка формы никак не связана с отображением таблицы. Это сигнал, чтобы разделить компоненты, извлечь дочерние компоненты и превратить компонент страницы в оркестратор нескольких дочерних компонентов.
Не существует правильного или неправильного подхода, и требуется опыт, чтобы понять, что работает лучше всего. Одно из полезных правил - выделять то, что связано друг с другом, и ценить связность больше, чем размер (количество строк кода).
3. Реализация независимого режима рендеринга для Blazor-компонентов
С появлением интерактивного режима рендеринга в .NET 8 мы получаем гораздо больше гибкости по сравнению с предыдущими версиями Blazor. Например, мы можем реализовать веб-приложение, полностью отрисовываемое на сервере, без какой-либо интерактивности. Или можем комбинировать интерактивность Blazor Server и Blazor WebAssembly в одном приложении.
Для обеспечения гибкой архитектуры рекомендуется настроить компоненты Blazor так, чтобы они не зависели от режима рендеринга. Т.е. не задавать тип интерактивности внутри каждого компонента, а задавать его только на верхнем уровне. Это позволяет использовать компонент как часть интерактивного приложения Blazor Server и Blazor WebAssembly.
4. Изучите правильную обработку событий
Узнайте, как привязывать методы C# к событиям, вызываемым HTML-элементами. Это фундаментальный механизм для реализации обработчиков onClick для кнопок или обработчиков отправки для HTML-форм. При регистрации событий .NET, таких как событие LocationChanged класса NavigationManager, обязательно отписывайтесь от события при удалении компонента. В противном случае компонент не будет уничтожен сборщиком мусора.
@implements IDisposable
@inject NavigationManager NavigationManager
protected override void OnInitialized()
{
NavigationManager.LocationChanged
+= LocationChanged;
}
void LocationChanged(
object sender,
LocationChangedEventArgs e)
{
System.WriteLine("Location changed");
}
void IDisposable.Dispose()
{
NavigationManager.LocationChanged
-= LocationChanged;
}
Что касается связи между компонентами, обратные вызовы событий (EventCallback) являются стандартным способом осуществления обратного вызова родительского компонента из дочернего компонента.
Окончание следует…
Источник: https://www.telerik.com/blogs/blazor-basics-9-best-practices-building-blazor-web-applications
👍6
День 2450. #TipsAndTricks #Blazor
Лучшие Практики по Созданию Веб-Приложений в Blazor. Окончание
Начало
5. Выберите подходящий метод управления состоянием
Blazor предлагает различные варианты управления состоянием. Параметры компонентов — самый простой вариант, за которым следуют каскадные значения и извлечение состояния в специализированные реализации сервисов.
Для больших приложений может подойти библиотека управления состоянием, например, Fluxor, или другой контейнер глобального состояния. Однако имейте в виду, что обработка глобального состояния может привести к сложностям в приложении.
6. Правильная организация и структура кода
Используйте чёткую, понятную и организованную структуру кода. Например, группируйте связанные компоненты по папкам, а сервисы и страницы — в логические папки.
Также следуйте рекомендациям по именованию компонентов, таким как соглашения об именовании, и разделяйте задачи путём извлечения компонентов, чтобы повысить удобство поддержки всего приложения.
Новый шаблон веб-приложения Blazor в .NET 8 - хорошая отправная точка. Однако обязательно реорганизуйте код, когда приложение значительно разрастётся в той или иной области, чтобы не приходилось постоянно искать связанные части кода.
7. Защитите приложение
Ознакомьтесь с лучшими практиками веб-безопасности, такими как OSWASP Top 10, и примите меры, особенно при работе с конфиденциальными данными.
Используйте аутентификацию и авторизацию ASP.NET Core для защиты доступа к конечным точкам и страницам Blazor. Храните только ту информацию, которая необходима для выполнения ваших бизнес-задач. Всегда используйте HTTPS.
Не храните пароли пользователей самостоятельно. Используйте провайдер аутентификации. Если нет другого варианта и приходится хранить учётные записи пользователей самостоятельно, убедитесь, что пароли правильно хэшируются с помощью надежного алгоритма хэширования, например, BCrypt.
8. Используйте правильную обработку ошибок и ведение журнала
Реализуйте надёжное решение для обработки ошибок и исключений. Убедитесь, что логи содержат важную информацию для решения проблем в коде. В то же время избегайте регистрации конфиденциальной информации и заменяйте ее плейсхолдерами.
Вы можете использовать фреймворк логирования ASP.NET Core или добавить более гибкое и эффективное решение, например, Serilog.
9. Максимально простое решение
Один из самых недооценённых советов как в разработке ПО в целом, так и в разработке на Blazor — это простота. Существует множество сложных реализаций, которые можно заменить простыми решениями. Всегда стремитесь реализовать максимально простое решение любой задачи.
Например, когда нужно передать значение компоненту, начните с использования параметра компонента. Зачем реализовывать сложный сервис и внедрять его в дочерний компонент, если можно решить проблему с помощью простого параметра компонента?
Источник: https://www.telerik.com/blogs/blazor-basics-9-best-practices-building-blazor-web-applications
Лучшие Практики по Созданию Веб-Приложений в Blazor. Окончание
Начало
5. Выберите подходящий метод управления состоянием
Blazor предлагает различные варианты управления состоянием. Параметры компонентов — самый простой вариант, за которым следуют каскадные значения и извлечение состояния в специализированные реализации сервисов.
Для больших приложений может подойти библиотека управления состоянием, например, Fluxor, или другой контейнер глобального состояния. Однако имейте в виду, что обработка глобального состояния может привести к сложностям в приложении.
6. Правильная организация и структура кода
Используйте чёткую, понятную и организованную структуру кода. Например, группируйте связанные компоненты по папкам, а сервисы и страницы — в логические папки.
Также следуйте рекомендациям по именованию компонентов, таким как соглашения об именовании, и разделяйте задачи путём извлечения компонентов, чтобы повысить удобство поддержки всего приложения.
Новый шаблон веб-приложения Blazor в .NET 8 - хорошая отправная точка. Однако обязательно реорганизуйте код, когда приложение значительно разрастётся в той или иной области, чтобы не приходилось постоянно искать связанные части кода.
7. Защитите приложение
Ознакомьтесь с лучшими практиками веб-безопасности, такими как OSWASP Top 10, и примите меры, особенно при работе с конфиденциальными данными.
Используйте аутентификацию и авторизацию ASP.NET Core для защиты доступа к конечным точкам и страницам Blazor. Храните только ту информацию, которая необходима для выполнения ваших бизнес-задач. Всегда используйте HTTPS.
Не храните пароли пользователей самостоятельно. Используйте провайдер аутентификации. Если нет другого варианта и приходится хранить учётные записи пользователей самостоятельно, убедитесь, что пароли правильно хэшируются с помощью надежного алгоритма хэширования, например, BCrypt.
8. Используйте правильную обработку ошибок и ведение журнала
Реализуйте надёжное решение для обработки ошибок и исключений. Убедитесь, что логи содержат важную информацию для решения проблем в коде. В то же время избегайте регистрации конфиденциальной информации и заменяйте ее плейсхолдерами.
Вы можете использовать фреймворк логирования ASP.NET Core или добавить более гибкое и эффективное решение, например, Serilog.
9. Максимально простое решение
Один из самых недооценённых советов как в разработке ПО в целом, так и в разработке на Blazor — это простота. Существует множество сложных реализаций, которые можно заменить простыми решениями. Всегда стремитесь реализовать максимально простое решение любой задачи.
Например, когда нужно передать значение компоненту, начните с использования параметра компонента. Зачем реализовывать сложный сервис и внедрять его в дочерний компонент, если можно решить проблему с помощью простого параметра компонента?
Источник: https://www.telerik.com/blogs/blazor-basics-9-best-practices-building-blazor-web-applications
2👍5
День 2451. #Карьера
Я Разработчик Средних Лет, и Мои Лучшие Качества… Изменились
И то, что для меня важно, тоже изменилось.
Автор оригинала: Jeffrey Bakker
Языки, которые я использовал, платформы, на которых я работал, и решённые бизнес-задачи невероятно разнообразны. Мне нравится разработка функций, архитектура, покрытие кода, тестирование, CI/CD, UI и даже документация. Всё должно быть хорошо, так о чём же эта история?
Мой мозг изменился
В среднем возрасте мой технический фокус сместился. Я меньше одержим погоней за каждым новым инструментом и больше сосредоточен на глубине и ясности. Мой интеллект способен справиться с большей широтой, но мой фокус рассеян; я осознаю гораздо больше, о чём раньше не беспокоился. Я не запоминаю детали, как раньше, но быстрее связываю идеи и вижу закономерности, которые раньше упускал из виду.
Я обменял часть своих технических навыков на более «человеческие», и это открыло мне больше дверей. Мне приходится чаще использовать навыки общения. Как интроверта, любившего прятаться в своей работе, эта мысль раньше меня пугала.
Стало не хуже, просто по-другому
Хваля молодых разработчиков за их новые и захватывающие функции, я с теплотой скучаю по той же похвале, но не скучаю по давлению, которое часто с ней связано. Да, я всё ещё иногда работаю сверхурочно, но мне нравится делать это ради тех задач, которые мне нравятся.
Я бывал в центре внимания, но никогда не стремился к нему. Иногда рутинная работа сама по себе награда. Я всегда думаю о том, как помочь нашей команде. Мне бы хотелось работать над темой, которая соответствует моим средне- и долгосрочным карьерным целям, а не над темой месяца.
Мой золотой век был период с 2012 по 2019 год, охватывающий три софтверные корпорации. До этого я уже разрабатывал ПО типичными неправильными способами. Поначалу я отрицал и сопротивлялся некоторым передовым практикам (отвыкать от привычек сложно). Но в итоге я стал лучше, начал отстаивать все передовые практики. Меня слышали и уважали, потому что я добился успеха. Несколько лет спустя я выгорел, пытаясь изменить компанию, которая находилась в таком же состоянии отрицания, что и я сам несколько лет назад.
Мне не нужно переживать прошлое
Каждый из нас вспоминал времена, когда всё шло идеально. Спустя десять лет работы сеньором, я могу сказать, что я-«молодой сеньор», много знал о технологиях, но мало о людях и корпорациях. Я мог действовать самостоятельно, когда позволяли, но моё видение ограничивалось тем, что мне было позволено видеть. Я был способным и всесторонне развитым технически, но в бизнесе у меня был эффект Даннинга-Крюгера.
Что меня ждёт в будущем?
Лет 10 назад один из моих тимлидов углубился в управление персоналом, признав, что он «слишком стар, чтобы идти в ногу с новейшими технологиями и практиками». Другой менеджер, наоборот, сказал, что выбрал менеджмент, потому что разработка со временем стала скучной. Ему нужен был новый вызов.
Я всегда думал, что пойду по пути техлида. Но я оставил стек, на котором специализировался несколько лет. Сейчас я вряд ли могу продемонстрировать какие-либо лидерские качества в текущем стеке. DevOps выглядит привлекательно, поскольку соответствует тому, что я считаю важным. Хотя я бы скучал по временам, когда был и разработчиком функций, и тестировщиком, и DevOpsом.
Итого
У меня была полноценная карьера. Я не сияю так ярко, как раньше, но я стал мудрее. Молодым разработчикам есть на что опереться, чтобы они могли сиять. Никогда ещё технологии разработки ПО не были так многослойны и многообразны. Работа с современными языками программирования может доставлять удовольствие. Мы достаточно раз терпели неудачи, чтобы показать вам, что не работает, и у нас достаточно примеров того, что работает стабильно. Ваша главная задача сейчас — выделяться. Сияйте ярко!
PS: Напишите в комментариях, как изменились ваши приоритеты в карьере с течением времени.
Источник: https://levelup.gitconnected.com/im-a-middle-aged-developer-and-my-time-to-shine-has-sunset-a1b5d5c6ff2d
Я Разработчик Средних Лет, и Мои Лучшие Качества… Изменились
И то, что для меня важно, тоже изменилось.
Автор оригинала: Jeffrey Bakker
Языки, которые я использовал, платформы, на которых я работал, и решённые бизнес-задачи невероятно разнообразны. Мне нравится разработка функций, архитектура, покрытие кода, тестирование, CI/CD, UI и даже документация. Всё должно быть хорошо, так о чём же эта история?
Мой мозг изменился
В среднем возрасте мой технический фокус сместился. Я меньше одержим погоней за каждым новым инструментом и больше сосредоточен на глубине и ясности. Мой интеллект способен справиться с большей широтой, но мой фокус рассеян; я осознаю гораздо больше, о чём раньше не беспокоился. Я не запоминаю детали, как раньше, но быстрее связываю идеи и вижу закономерности, которые раньше упускал из виду.
Я обменял часть своих технических навыков на более «человеческие», и это открыло мне больше дверей. Мне приходится чаще использовать навыки общения. Как интроверта, любившего прятаться в своей работе, эта мысль раньше меня пугала.
Стало не хуже, просто по-другому
Хваля молодых разработчиков за их новые и захватывающие функции, я с теплотой скучаю по той же похвале, но не скучаю по давлению, которое часто с ней связано. Да, я всё ещё иногда работаю сверхурочно, но мне нравится делать это ради тех задач, которые мне нравятся.
Я бывал в центре внимания, но никогда не стремился к нему. Иногда рутинная работа сама по себе награда. Я всегда думаю о том, как помочь нашей команде. Мне бы хотелось работать над темой, которая соответствует моим средне- и долгосрочным карьерным целям, а не над темой месяца.
Мой золотой век был период с 2012 по 2019 год, охватывающий три софтверные корпорации. До этого я уже разрабатывал ПО типичными неправильными способами. Поначалу я отрицал и сопротивлялся некоторым передовым практикам (отвыкать от привычек сложно). Но в итоге я стал лучше, начал отстаивать все передовые практики. Меня слышали и уважали, потому что я добился успеха. Несколько лет спустя я выгорел, пытаясь изменить компанию, которая находилась в таком же состоянии отрицания, что и я сам несколько лет назад.
Мне не нужно переживать прошлое
Каждый из нас вспоминал времена, когда всё шло идеально. Спустя десять лет работы сеньором, я могу сказать, что я-«молодой сеньор», много знал о технологиях, но мало о людях и корпорациях. Я мог действовать самостоятельно, когда позволяли, но моё видение ограничивалось тем, что мне было позволено видеть. Я был способным и всесторонне развитым технически, но в бизнесе у меня был эффект Даннинга-Крюгера.
Что меня ждёт в будущем?
Лет 10 назад один из моих тимлидов углубился в управление персоналом, признав, что он «слишком стар, чтобы идти в ногу с новейшими технологиями и практиками». Другой менеджер, наоборот, сказал, что выбрал менеджмент, потому что разработка со временем стала скучной. Ему нужен был новый вызов.
Я всегда думал, что пойду по пути техлида. Но я оставил стек, на котором специализировался несколько лет. Сейчас я вряд ли могу продемонстрировать какие-либо лидерские качества в текущем стеке. DevOps выглядит привлекательно, поскольку соответствует тому, что я считаю важным. Хотя я бы скучал по временам, когда был и разработчиком функций, и тестировщиком, и DevOpsом.
Итого
У меня была полноценная карьера. Я не сияю так ярко, как раньше, но я стал мудрее. Молодым разработчикам есть на что опереться, чтобы они могли сиять. Никогда ещё технологии разработки ПО не были так многослойны и многообразны. Работа с современными языками программирования может доставлять удовольствие. Мы достаточно раз терпели неудачи, чтобы показать вам, что не работает, и у нас достаточно примеров того, что работает стабильно. Ваша главная задача сейчас — выделяться. Сияйте ярко!
PS: Напишите в комментариях, как изменились ваши приоритеты в карьере с течением времени.
Источник: https://levelup.gitconnected.com/im-a-middle-aged-developer-and-my-time-to-shine-has-sunset-a1b5d5c6ff2d
👍12👎1
День 2452. #ВопросыНаСобеседовании
Марк Прайс в своей книге предложил свой набор из 60 вопросов на собеседовании.
5. Свойства и индексаторы
«Можете ли вы объяснить разницу между свойствами и индексаторами в C# и привести примеры ситуаций, в которых каждый из них может быть использован?»
Хороший ответ
«В C# свойства и индексаторы используются для инкапсуляции данных, но они служат разным целям и используются в разных контекстах. Свойства действуют как комбинация метода и поля, предоставляя способ получения и установки значений с дополнительной логикой, используя синтаксис, подобный синтаксису полей. Свойства наиболее подходят, когда требуется предоставить данные класса с потенциальной проверкой, вычислением или преобразованием. Например, класс Person может иметь свойство DateOfBirth, которое гарантирует, что дата установлена в прошлом, и вычисляет возраст человека при получении.
Индексаторы позволяют индексировать объект как массив, хотя ключ может быть любого типа данных, а не только целочисленным. Это особый тип свойства, позволяющий получать доступ к классам с помощью оператора доступа к массиву []. Индексаторы особенно полезны, когда класс представляет собой коллекцию элементов. Например, класс Library может использовать индексатор, чтобы предоставить клиентам доступ к книгам по числовому индексу.
Как свойства, так и индексаторы включают методы доступа get и set (или только один из них), и оба могут включать в себя дополнительную логику в этих методах для обеспечения инкапсуляции или управления побочными эффектами.»
Часто встречающийся неточный ответ:
«Я использую свойства, когда мне нужно хранить данные в полях, и индексаторы, когда я хочу использовать массивы в своих классах».
Этот ответ отражает непонимание как свойств, так и индексаторов:
- Непонимание свойств: Свойства предназначены не только для хранения данных; они используются для инкапсуляции доступа к данным и могут включать логику проверки, значения по умолчанию или другие преобразования. Это не просто поля, а более сложные функции.
- Непонимание индексаторов: Индексаторы — это не просто способ реализации массивов внутри классов. Они позволяют индексировать экземпляр класса подобно массивам, но и служат для того, чтобы класс вёл себя как коллекция. Это непонимание принижает роль индексаторов в абстракции и инкапсуляции.
Ошибка обычно возникает из-за отсутствия глубокого понимания принципов объектно-ориентированного программирования и роли, которую эти структуры играют в обеспечении целостности данных и создании гибкой, поддерживаемой архитектуры кода.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Марк Прайс в своей книге предложил свой набор из 60 вопросов на собеседовании.
5. Свойства и индексаторы
«Можете ли вы объяснить разницу между свойствами и индексаторами в C# и привести примеры ситуаций, в которых каждый из них может быть использован?»
Хороший ответ
«В C# свойства и индексаторы используются для инкапсуляции данных, но они служат разным целям и используются в разных контекстах. Свойства действуют как комбинация метода и поля, предоставляя способ получения и установки значений с дополнительной логикой, используя синтаксис, подобный синтаксису полей. Свойства наиболее подходят, когда требуется предоставить данные класса с потенциальной проверкой, вычислением или преобразованием. Например, класс Person может иметь свойство DateOfBirth, которое гарантирует, что дата установлена в прошлом, и вычисляет возраст человека при получении.
Индексаторы позволяют индексировать объект как массив, хотя ключ может быть любого типа данных, а не только целочисленным. Это особый тип свойства, позволяющий получать доступ к классам с помощью оператора доступа к массиву []. Индексаторы особенно полезны, когда класс представляет собой коллекцию элементов. Например, класс Library может использовать индексатор, чтобы предоставить клиентам доступ к книгам по числовому индексу.
Как свойства, так и индексаторы включают методы доступа get и set (или только один из них), и оба могут включать в себя дополнительную логику в этих методах для обеспечения инкапсуляции или управления побочными эффектами.»
Часто встречающийся неточный ответ:
«Я использую свойства, когда мне нужно хранить данные в полях, и индексаторы, когда я хочу использовать массивы в своих классах».
Этот ответ отражает непонимание как свойств, так и индексаторов:
- Непонимание свойств: Свойства предназначены не только для хранения данных; они используются для инкапсуляции доступа к данным и могут включать логику проверки, значения по умолчанию или другие преобразования. Это не просто поля, а более сложные функции.
- Непонимание индексаторов: Индексаторы — это не просто способ реализации массивов внутри классов. Они позволяют индексировать экземпляр класса подобно массивам, но и служат для того, чтобы класс вёл себя как коллекция. Это непонимание принижает роль индексаторов в абстракции и инкапсуляции.
Ошибка обычно возникает из-за отсутствия глубокого понимания принципов объектно-ориентированного программирования и роли, которую эти структуры играют в обеспечении целостности данных и создании гибкой, поддерживаемой архитектуры кода.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👍10
День 2453. #МоиИнструменты
RazorConsole
Если среди нас есть те, кто скучает по временам Norton Commander, терпеть не может мышь и кому «все эти ваши UI как кость в горле, дайте консоль», у меня для вас хорошие новости. NuGet-пакет RazorConsole стирает грань между разработкой современных веб-UI и консольными приложениями. Он позволяет создавать сложные интерфейсы в консоли с использованием компонентов Razor.
Стандартный пример в шаблоне приложения Blazor – компонент счётчика. Посмотрим, как это выглядит в RazorConsole.
Для начала создадим новый консольный проект. Добавим в него NuGet-пакет RazorConsole:
Кроме того, RazorConsole требуется SDK Microsoft.NET.Sdk.Razor для компиляции компонентов Razor. Поэтому нужно обновить файл проекта (.csproj) для его использования:
Добавим простой компонент Razor в файл
Мы разместили текст, элемент счётчика и 2 кнопки (увеличивающую и уменьшающую счётчик). Всё это мы поместили в простую таблицу, используя готовые компоненты
В файле
Пример работы показан на видео ниже. Активная кнопка выделяется голубым, перемещаться можно клавишей
Таким образом можно создавать UI в консоли, используя знакомые компоненты Razor с полной поддержкой привязки данных, обработки событий и методов жизненного цикла компонентов. Пакет содержит 15 готовых компонентов, охватывающих все необходимые функции:
- Разметка: Grid, Columns, Rows, Align, Padder;
- Поля ввода: TextInput, TextButton, Select;
- Отображение: Markup, Panel, Border, Figlet, SyntaxHighlighter, Table;
- Утилиты: Spinner, Newline.
Также имеется интерактивная галерея компонентов, которая поставляется как dotnet утилита RazorConsole.Gallery. Она содержит документацию по всем компонентам.
Источник: https://github.com/LittleLittleCloud/RazorConsole/
RazorConsole
Если среди нас есть те, кто скучает по временам Norton Commander, терпеть не может мышь и кому «все эти ваши UI как кость в горле, дайте консоль», у меня для вас хорошие новости. NuGet-пакет RazorConsole стирает грань между разработкой современных веб-UI и консольными приложениями. Он позволяет создавать сложные интерфейсы в консоли с использованием компонентов Razor.
Стандартный пример в шаблоне приложения Blazor – компонент счётчика. Посмотрим, как это выглядит в RazorConsole.
Для начала создадим новый консольный проект. Добавим в него NuGet-пакет RazorConsole:
dotnet add package RazorConsole.Core
Кроме того, RazorConsole требуется SDK Microsoft.NET.Sdk.Razor для компиляции компонентов Razor. Поэтому нужно обновить файл проекта (.csproj) для его использования:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<!-- … -->
</Project>
Добавим простой компонент Razor в файл
Counter.razor
:@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Web
@using RazorConsole.Components
<Rows>
<Columns>
<p>Current count</p>
<Markup Content="@count.ToString()"
Foreground="@Spectre.Console.Color.Green" />
</Columns>
<Columns>
<TextButton Content="+1"
OnClick="Increment"
BackgroundColor="@Spectre.Console.Color.Grey"
FocusedColor="@Spectre.Console.Color.Blue" />
<TextButton Content="-1"
OnClick="Decrement"
BackgroundColor="@Spectre.Console.Color.Grey"
FocusedColor="@Spectre.Console.Color.Blue" />
</Columns>
</Rows>
@code {
private int count = 0;
private void Increment() => count++;
private void Decrement() => count--;
}
Мы разместили текст, элемент счётчика и 2 кнопки (увеличивающую и уменьшающую счётчик). Всё это мы поместили в простую таблицу, используя готовые компоненты
Rows
и Columns
(о них позже).В файле
Program.cs
нам осталось добавить всего одну строку:await AppHost.RunAsync<Counter>();
Пример работы показан на видео ниже. Активная кнопка выделяется голубым, перемещаться можно клавишей
Tab
, а нажимать на кнопку клавишей Enter
.Таким образом можно создавать UI в консоли, используя знакомые компоненты Razor с полной поддержкой привязки данных, обработки событий и методов жизненного цикла компонентов. Пакет содержит 15 готовых компонентов, охватывающих все необходимые функции:
- Разметка: Grid, Columns, Rows, Align, Padder;
- Поля ввода: TextInput, TextButton, Select;
- Отображение: Markup, Panel, Border, Figlet, SyntaxHighlighter, Table;
- Утилиты: Spinner, Newline.
Также имеется интерактивная галерея компонентов, которая поставляется как dotnet утилита RazorConsole.Gallery. Она содержит документацию по всем компонентам.
Источник: https://github.com/LittleLittleCloud/RazorConsole/
This media is not supported in your browser
VIEW IN TELEGRAM
👍12
День 2454. #ЧтоНовенького
Вышел .NET 10 Release Candidate 2
Microsoft выпустили .NET 10 Release Candidate 2, финальную предварительную сборку перед релизом. Как сообщает команда .NET, RC 2 поставляется с лицензией на поддержку go-live, что позволяет разворачивать продуктовую версию и одновременно проверять платформу перед её официальным релизом. Сборка поддерживается в Visual Studio 2026 Insiders и Visual Studio Code с помощью C# Dev Kit.
Официальная дата выпуска .NET 10 — 11 ноября 2025 года. Это будет релиз с долгосрочной поддержкой (LTS), обеспечивающий три года исправлений и обновлений. Release Candidate 1 (RC1) был доступен 9 сентября, а RC2 — 14 октября. В Microsoft заявили, что этот релиз в первую очередь ориентирован на валидацию, качество и стабильность, а не на добавление новых функций. Команда сделала акцент на постепенном улучшении качества, чтобы обеспечить плавный переход к GA и совместимость со всеми поддерживаемыми рабочими нагрузками.
1. MAUI
Windows теперь поддерживает разрешения на доступ к микрофону через Permissions.RequestAsync<Permissions.Microphone>(), обеспечивая единую модель разрешений для всех платформ. Android получает поддержку SafeAreaEdges, что улучшает поведение макета при отрисовке от края до края и наложениях клавиатуры.
Также улучшили генерацию исходного кода XAML, предлагающую более быструю отладку генерации представлений и упрощённый механизм настройки через свойство <MauiXamlInflator>SourceGen</MauiXamlInflator>. В Microsoft описали эти обновления как часть продолжающейся работы по повышению производительности и предсказуемости разработки MAUI.
Для Android, в RC 2 представлены привязки API 36.1, разработанные совместно с командой платформы Uno. Проекты могут быть ориентированы на net10.0-android36.1 для доступа к новейшим API платформы, при этом EnablePreviewFeatures по-прежнему временно требуется. В этом выпуске также продолжается экспериментальное внедрение CoreCLR для Android, позволяя разработчикам отключать Mono (UseMonoRuntime=false) и запускать приложения в новой среде выполнения. Хотя эта функция пока не готова к использованию в промышленной среде, по заявлению Microsoft, она представляет собой важный шаг к унификации среды выполнения на разных платформах.
Для разработчиков Apple теперь доступны привязки Xcode 26 для .NET для iOS, macOS, Mac Catalyst и tvOS, что обеспечивает совместимость с новейшими SDK Apple и единообразие между целевыми платформами .NET 9 и .NET 10.
2. Entity Framework Core
Добавлены обновления стабильности и надёжности, такие как улучшенная обработка сложных сопоставлений JSON, уточнённые границы транзакций миграции, поддержка повторных запросов через ExecutionStrategy и новые предупреждения анализатора о небезопасной конкатенации SQL.
3. SDK
Добавлена возможность запускать задачи MSBuild на базе dotnet в Visual Studio и msbuild.exe, устраняя давний разрыв между средами сборки dotnet и .NET Framework. Объявляя задачи с Runtime="NET" и TaskFactory="TaskHostFactory", авторы могут повторно использовать одну и ту же реализацию в CLI и IDE без необходимости настройки на несколько платформ.
Как пояснила команда разработчиков, эта функция знаменует собой первый шаг в более масштабной модернизации MSBuild. В будущих выпусках планируется добавить дополнительные возможности в MSBuild, чтобы упростить написание и использование задач .NET, включая:
- Автоматическое обнаружение и загрузку задач .NET без необходимости указания метаданных среды выполнения или TaskFactory;
- Снижение нагрузки на производительность IPC между движком MSBuild и задачами при выполнении вне процесса;
- Поддержку функции Host Object для задач .NET, выполняемых вне процесса.
.NET 10 RC 2 предназначена для проверки качества релиза, что открывает путь к полноценному релизу в следующем месяце. Разработчикам рекомендуется протестировать приложения с RC 2 и поделиться отзывами в официальном обсуждении на GitHub до релиза .NET 10 — 11 ноября 2025 года.
Источник: https://www.infoq.com/news/2025/10/dotnet-10-rc-2-release/
Вышел .NET 10 Release Candidate 2
Microsoft выпустили .NET 10 Release Candidate 2, финальную предварительную сборку перед релизом. Как сообщает команда .NET, RC 2 поставляется с лицензией на поддержку go-live, что позволяет разворачивать продуктовую версию и одновременно проверять платформу перед её официальным релизом. Сборка поддерживается в Visual Studio 2026 Insiders и Visual Studio Code с помощью C# Dev Kit.
Официальная дата выпуска .NET 10 — 11 ноября 2025 года. Это будет релиз с долгосрочной поддержкой (LTS), обеспечивающий три года исправлений и обновлений. Release Candidate 1 (RC1) был доступен 9 сентября, а RC2 — 14 октября. В Microsoft заявили, что этот релиз в первую очередь ориентирован на валидацию, качество и стабильность, а не на добавление новых функций. Команда сделала акцент на постепенном улучшении качества, чтобы обеспечить плавный переход к GA и совместимость со всеми поддерживаемыми рабочими нагрузками.
1. MAUI
Windows теперь поддерживает разрешения на доступ к микрофону через Permissions.RequestAsync<Permissions.Microphone>(), обеспечивая единую модель разрешений для всех платформ. Android получает поддержку SafeAreaEdges, что улучшает поведение макета при отрисовке от края до края и наложениях клавиатуры.
Также улучшили генерацию исходного кода XAML, предлагающую более быструю отладку генерации представлений и упрощённый механизм настройки через свойство <MauiXamlInflator>SourceGen</MauiXamlInflator>. В Microsoft описали эти обновления как часть продолжающейся работы по повышению производительности и предсказуемости разработки MAUI.
Для Android, в RC 2 представлены привязки API 36.1, разработанные совместно с командой платформы Uno. Проекты могут быть ориентированы на net10.0-android36.1 для доступа к новейшим API платформы, при этом EnablePreviewFeatures по-прежнему временно требуется. В этом выпуске также продолжается экспериментальное внедрение CoreCLR для Android, позволяя разработчикам отключать Mono (UseMonoRuntime=false) и запускать приложения в новой среде выполнения. Хотя эта функция пока не готова к использованию в промышленной среде, по заявлению Microsoft, она представляет собой важный шаг к унификации среды выполнения на разных платформах.
Для разработчиков Apple теперь доступны привязки Xcode 26 для .NET для iOS, macOS, Mac Catalyst и tvOS, что обеспечивает совместимость с новейшими SDK Apple и единообразие между целевыми платформами .NET 9 и .NET 10.
2. Entity Framework Core
Добавлены обновления стабильности и надёжности, такие как улучшенная обработка сложных сопоставлений JSON, уточнённые границы транзакций миграции, поддержка повторных запросов через ExecutionStrategy и новые предупреждения анализатора о небезопасной конкатенации SQL.
3. SDK
Добавлена возможность запускать задачи MSBuild на базе dotnet в Visual Studio и msbuild.exe, устраняя давний разрыв между средами сборки dotnet и .NET Framework. Объявляя задачи с Runtime="NET" и TaskFactory="TaskHostFactory", авторы могут повторно использовать одну и ту же реализацию в CLI и IDE без необходимости настройки на несколько платформ.
Как пояснила команда разработчиков, эта функция знаменует собой первый шаг в более масштабной модернизации MSBuild. В будущих выпусках планируется добавить дополнительные возможности в MSBuild, чтобы упростить написание и использование задач .NET, включая:
- Автоматическое обнаружение и загрузку задач .NET без необходимости указания метаданных среды выполнения или TaskFactory;
- Снижение нагрузки на производительность IPC между движком MSBuild и задачами при выполнении вне процесса;
- Поддержку функции Host Object для задач .NET, выполняемых вне процесса.
.NET 10 RC 2 предназначена для проверки качества релиза, что открывает путь к полноценному релизу в следующем месяце. Разработчикам рекомендуется протестировать приложения с RC 2 и поделиться отзывами в официальном обсуждении на GitHub до релиза .NET 10 — 11 ноября 2025 года.
Источник: https://www.infoq.com/news/2025/10/dotnet-10-rc-2-release/
👍2
День 2455. #ЗаметкиНаПолях
Когда Type.FullName Возвращает null
Немного бесполезной информации вам в ленту. Казалось бы, имя типа в .NET всегда должно быть известно. Однако сигнатура метода Type.FullName такая:
Такое поведение может показаться неожиданным, но существуют определённые сценарии, в которых среда выполнения .NET не может сгенерировать корректное полное имя для типа. Вот два основных случая, когда Type.FullName возвращает null.
1. Обобщённые типы с открытыми параметрами типа
При создании обобщённого типа, содержащего несвязанные обобщённые параметры:
2. Указатели на функции
Указатели на функции, введённые в C#9, также имеют null в FullName:
Источник: https://www.meziantou.net/understanding-when-type-fullname-returns-null-in-dotnet.htm
Когда Type.FullName Возвращает null
Немного бесполезной информации вам в ленту. Казалось бы, имя типа в .NET всегда должно быть известно. Однако сигнатура метода Type.FullName такая:
public abstract string? FullName { get; }
Такое поведение может показаться неожиданным, но существуют определённые сценарии, в которых среда выполнения .NET не может сгенерировать корректное полное имя для типа. Вот два основных случая, когда Type.FullName возвращает null.
1. Обобщённые типы с открытыми параметрами типа
При создании обобщённого типа, содержащего несвязанные обобщённые параметры:
var list = typeof(IList<>);
var dict = typeof(IDictionary<,>);
var listOfDictionaries = list.MakeGenericType(dict);
// IList<IDictionary<,>>
Assert.Null(listOfDictionaries.FullName);
2. Указатели на функции
Указатели на функции, введённые в C#9, также имеют null в FullName:
var functionPointerType = typeof(delegate*<int, void>);
Assert.Null(functionPointerType.FullName);
Источник: https://www.meziantou.net/understanding-when-type-fullname-returns-null-in-dotnet.htm
👍13
День 2456. #TipsAndTricks
6 Шагов для Правильной Настройки Нового .NET-проекта. Начало
Начинать новый .NET-проект всегда волнительно. Но легко пропустить подготовительную работу, которая делает проект масштабируемым и поддерживаемым. Вот несколько ключевых шагов, которые значительно облегчат вам (и вашим коллегам) жизнь в дальнейшем.
1. Единый стиль кода
Файл .editorconfig гарантирует, что все участники команды будут использовать одинаковые соглашения по форматированию и именованию, что позволяет избежать несоответствий в отступах и случайных правил именования.
Можно создать его прямо в Visual Studio. Щелкните правой кнопкой на решении Add -> New Editorconfig (Добавить -> Новый Editorconfig). Конфигурация по умолчанию — отличное начало. Но вы можете настроить её дополнительно в соответствии с предпочтениями команды. Разместите файл в корне решения, чтобы все проекты следовали одним и тем же правилам. При необходимости можно переопределить определённые настройки во вложенных папках, поместив туда свой файл .editorconfig. Вот пара примеров:
- из репозитория среды исполнения .NET
- от Милана общий для проектов .NET
2. Централизованная конфигурации сборки
Файл Directory.Build.props позволяет определить параметры сборки, применяемые к каждому проекту в решении:
Это позволяет сохранить чистоту и единообразие ваших файлов .csproj, поскольку нет необходимости повторять эти свойства в каждом проекте. Если позже вы захотите включить статические анализаторы или настроить параметры сборки, вы можете сделать это один раз здесь. Преимущество в том, что файлы .csproj становятся практически пустыми, большую часть времени содержащими только ссылки на NuGet-пакеты.
3. Централизованное управление пакетами
По мере роста решения управление версиями NuGet-пакетов в нескольких проектах становится проблематичным. Именно здесь на помощь приходит централизованное управление пакетами. Создайте файл с именем Directory.Packages.props в корне:
Теперь, когда нужно сослаться на NuGet-пакет в проекте, не нужно указывать версию. Можно использовать только имя пакета:
Всё управление версиями осуществляется централизованно. Это упрощает обновление зависимостей и позволяет избежать дрейфа версий между проектами. При необходимости вы по-прежнему можете переопределять версии в отдельных проектах.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/6-steps-for-setting-up-a-new-dotnet-project-the-right-way
6 Шагов для Правильной Настройки Нового .NET-проекта. Начало
Начинать новый .NET-проект всегда волнительно. Но легко пропустить подготовительную работу, которая делает проект масштабируемым и поддерживаемым. Вот несколько ключевых шагов, которые значительно облегчат вам (и вашим коллегам) жизнь в дальнейшем.
1. Единый стиль кода
Файл .editorconfig гарантирует, что все участники команды будут использовать одинаковые соглашения по форматированию и именованию, что позволяет избежать несоответствий в отступах и случайных правил именования.
Можно создать его прямо в Visual Studio. Щелкните правой кнопкой на решении Add -> New Editorconfig (Добавить -> Новый Editorconfig). Конфигурация по умолчанию — отличное начало. Но вы можете настроить её дополнительно в соответствии с предпочтениями команды. Разместите файл в корне решения, чтобы все проекты следовали одним и тем же правилам. При необходимости можно переопределить определённые настройки во вложенных папках, поместив туда свой файл .editorconfig. Вот пара примеров:
- из репозитория среды исполнения .NET
- от Милана общий для проектов .NET
2. Централизованная конфигурации сборки
Файл Directory.Build.props позволяет определить параметры сборки, применяемые к каждому проекту в решении:
<Project>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
Это позволяет сохранить чистоту и единообразие ваших файлов .csproj, поскольку нет необходимости повторять эти свойства в каждом проекте. Если позже вы захотите включить статические анализаторы или настроить параметры сборки, вы можете сделать это один раз здесь. Преимущество в том, что файлы .csproj становятся практически пустыми, большую часть времени содержащими только ссылки на NuGet-пакеты.
3. Централизованное управление пакетами
По мере роста решения управление версиями NuGet-пакетов в нескольких проектах становится проблематичным. Именно здесь на помощь приходит централизованное управление пакетами. Создайте файл с именем Directory.Packages.props в корне:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
<PackageVersion Include="SonarAnalyzer.CSharp" Version="10.15.0.120848" />
</ItemGroup>
</Project>
Теперь, когда нужно сослаться на NuGet-пакет в проекте, не нужно указывать версию. Можно использовать только имя пакета:
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
Всё управление версиями осуществляется централизованно. Это упрощает обновление зависимостей и позволяет избежать дрейфа версий между проектами. При необходимости вы по-прежнему можете переопределять версии в отдельных проектах.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/6-steps-for-setting-up-a-new-dotnet-project-the-right-way
1👍34
День 2457. #TipsAndTricks
6 Шагов для Правильной Настройки Нового .NET-проекта. Окончание
Начало
4. Статический анализ кода
Помогает выявлять потенциальные ошибки и поддерживать качество кода. В .NET есть набор встроенных анализаторов, но есть отличный NuGet-пакет SonarAnalyzer.CSharp для более полной проверки.
Также его можно добавить как глобальную ссылку в Directory.Build.props:
Это в сочетании с такими настройками:
…и ваши сборки будут завершаться неудачей при серьёзных недостатках качества кода. Это может быть отличной подстраховкой. Но поначалу это может мешать. Если некоторые правила не соответствуют вашему контексту, вы можете изменить или отключить их в файле .editorconfig, установив для важности правила значение none.
5. Настройка локальной оркестровки
Для обеспечения согласованности локальной среды в команде вам понадобится оркестровка контейнеров. Есть два основных варианта.
1) Docker Compose
Добавьте поддержку Docker Compose в Visual Studio. Будет добавлен файл docker-compose.yml, в котором вы можете определить сервисы:
Это позволит каждому разработчику локально развернуть один и тот же стек при помощи одной команды.
2) .NET Aspire
.NET Aspire выводит оркестровку на новый уровень. Он обеспечивает обнаружение сервисов, телеметрию и оптимизированную настройку, интегрированные с вашими проектами .NET. Вы можете добавить проект .NET и ресурс Postgres всего несколькими строками кода:
Aspire также использует Docker, но предоставляет более широкие возможности для разработки.
Не важно, Docker Compose или Aspire, цель одна: воспроизводимая, надёжная локальная конфигурация, которая работает одинаково на всех машинах.
6. Автоматизация сборки
Простой рабочий процесс GitHub Actions для проверки каждого коммита .github/workflows/build.yml:
Это гарантирует, что проект всегда будет собираться и проходить тесты, а проблемы будут выявляться до того, как они попадут в продакшн. Если сборка непрерывной интеграции (CI) завершится неудачей, вы сразу поймете, что что-то не так.
Что касается тестирования, изучите:
- Тестирование архитектуры,
- Интеграционное тестирование с Testcontainers.
Это даст уверенность в том, что код работает как ожидалось в среде, максимально приближенной к производственной.
Источник: https://www.milanjovanovic.tech/blog/6-steps-for-setting-up-a-new-dotnet-project-the-right-way
6 Шагов для Правильной Настройки Нового .NET-проекта. Окончание
Начало
4. Статический анализ кода
Помогает выявлять потенциальные ошибки и поддерживать качество кода. В .NET есть набор встроенных анализаторов, но есть отличный NuGet-пакет SonarAnalyzer.CSharp для более полной проверки.
Install-Package SonarAnalyzer.CSharp
Также его можно добавить как глобальную ссылку в Directory.Build.props:
<ItemGroup>
<PackageReference Include="SonarAnalyzer.CSharp" />
</ItemGroup>
Это в сочетании с такими настройками:
<Project>
<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisLevel>latest</AnalysisLevel>
<AnalysisMode>All</AnalysisMode>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
</Project>
…и ваши сборки будут завершаться неудачей при серьёзных недостатках качества кода. Это может быть отличной подстраховкой. Но поначалу это может мешать. Если некоторые правила не соответствуют вашему контексту, вы можете изменить или отключить их в файле .editorconfig, установив для важности правила значение none.
5. Настройка локальной оркестровки
Для обеспечения согласованности локальной среды в команде вам понадобится оркестровка контейнеров. Есть два основных варианта.
1) Docker Compose
Добавьте поддержку Docker Compose в Visual Studio. Будет добавлен файл docker-compose.yml, в котором вы можете определить сервисы:
services:
webapi:
build: .
postgres:
image: postgres:18
environment:
POSTGRES_PASSWORD: password
Это позволит каждому разработчику локально развернуть один и тот же стек при помощи одной команды.
2) .NET Aspire
.NET Aspire выводит оркестровку на новый уровень. Он обеспечивает обнаружение сервисов, телеметрию и оптимизированную настройку, интегрированные с вашими проектами .NET. Вы можете добавить проект .NET и ресурс Postgres всего несколькими строками кода:
var postgres = builder.AddPostgres("demo-db");
builder.AddProject<WebApi>("webapi")
.WithReference(postgres)
.WaitFor(postgres);
builder.Build().Run();
Aspire также использует Docker, но предоставляет более широкие возможности для разработки.
Не важно, Docker Compose или Aspire, цель одна: воспроизводимая, надёжная локальная конфигурация, которая работает одинаково на всех машинах.
6. Автоматизация сборки
Простой рабочий процесс GitHub Actions для проверки каждого коммита .github/workflows/build.yml:
name: Build
on:
push:
# Выполнение только на ветке main
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
- run: dotnet restore
- run: dotnet build --no-restore --configuration Release
- run: dotnet test --no-build --configuration Release
Это гарантирует, что проект всегда будет собираться и проходить тесты, а проблемы будут выявляться до того, как они попадут в продакшн. Если сборка непрерывной интеграции (CI) завершится неудачей, вы сразу поймете, что что-то не так.
Что касается тестирования, изучите:
- Тестирование архитектуры,
- Интеграционное тестирование с Testcontainers.
Это даст уверенность в том, что код работает как ожидалось в среде, максимально приближенной к производственной.
Источник: https://www.milanjovanovic.tech/blog/6-steps-for-setting-up-a-new-dotnet-project-the-right-way
👍15
День 2458. #ЧтоНовенького
Microsoft Исправила Самую Серьёзную Уязвимость в ASP.NET Core
На прошлой неделе Microsoft исправили уязвимость, получившую «самый высокий» уровень серьёзности из-за уязвимости безопасности ASP.NET Core.
Эта ошибка подмены HTTP-запросов (CVE-2025-55315) была обнаружена в веб-сервере Kestrel ASP.NET Core. Она позволяет аутентифицированным злоумышленникам подделывать другой HTTP-запрос для кражи учётных данных других пользователей или обхода средств безопасности на стороне клиента.
«Злоумышленник, успешно воспользовавшийся этой уязвимостью, может просматривать конфиденциальную информацию, такую как учётные данные других пользователей (Конфиденциальность), и вносить изменения в содержимое файлов на целевом сервере (Целостность), а также может вызвать сбой на сервере (Доступность)», — заявили в Microsoft во вторник в информационном бюллетене.
Для устранения этой уязвимости Microsoft выпустили обновления безопасности для Microsoft Visual Studio 2022, ASP.NET Core 2.3, ASP.NET Core 8.0 и ASP.NET Core 9.0, а также для пакета Microsoft.AspNetCore.Server.Kestrel.Core для приложений ASP.NET Core 2.x.
Чтобы защитить свои приложения ASP.NET Core от потенциальных атак, Microsoft рекомендует разработчикам и пользователям принять следующие меры:
- Если вы используете .NET 8 или более позднюю версию, установите обновление .NET из Центра обновления Microsoft, затем перезапустите приложение или перезагрузите компьютер.
- Если вы используете .NET 2.3, обновите ссылку на пакет Microsoft.AspNet.Server.Kestrel.Core до версии 2.3.6, затем перекомпилируйте и повторно разверните приложение.
- Если вы используете автономное/однофайловое приложение, установите обновление .NET, перекомпилируйте и повторно разверните.
Как пояснил руководитель технической программы безопасности .NET Барри Дорранс, последствия атак CVE-2025-55315 будут зависеть от целевого приложения ASP.NET, и успешная эксплуатация уязвимости может позволить злоумышленникам войти в систему под другим пользователем (для повышения привилегий), выполнить внутренний запрос (при атаках с подделкой запросов на стороне сервера), обойти проверки на подделку межсайтовых запросов (CSRF) или выполнить атаки с использованием инъекций.
«Мы не знаем, что из этого возможно, потому что всё зависит от того, как вы написали своё приложение. Поэтому мы оцениваем уровень риска, имея в виду наихудший возможный случай — обход функции безопасности, который позволяет изменить привилегии, — сказал Дорранс. — Вероятно ли это? Скорее всего, нет, если только код вашего приложения не делает что-то странное и не пропускает ряд проверок, которые он должен выполнять при каждом запросе. Тем не менее, пожалуйста, обновитесь».
Источник: https://www.bleepingcomputer.com/news/microsoft/microsoft-fixes-highest-severity-aspnet-core-flaw-ever/
Microsoft Исправила Самую Серьёзную Уязвимость в ASP.NET Core
На прошлой неделе Microsoft исправили уязвимость, получившую «самый высокий» уровень серьёзности из-за уязвимости безопасности ASP.NET Core.
Эта ошибка подмены HTTP-запросов (CVE-2025-55315) была обнаружена в веб-сервере Kestrel ASP.NET Core. Она позволяет аутентифицированным злоумышленникам подделывать другой HTTP-запрос для кражи учётных данных других пользователей или обхода средств безопасности на стороне клиента.
«Злоумышленник, успешно воспользовавшийся этой уязвимостью, может просматривать конфиденциальную информацию, такую как учётные данные других пользователей (Конфиденциальность), и вносить изменения в содержимое файлов на целевом сервере (Целостность), а также может вызвать сбой на сервере (Доступность)», — заявили в Microsoft во вторник в информационном бюллетене.
Для устранения этой уязвимости Microsoft выпустили обновления безопасности для Microsoft Visual Studio 2022, ASP.NET Core 2.3, ASP.NET Core 8.0 и ASP.NET Core 9.0, а также для пакета Microsoft.AspNetCore.Server.Kestrel.Core для приложений ASP.NET Core 2.x.
Чтобы защитить свои приложения ASP.NET Core от потенциальных атак, Microsoft рекомендует разработчикам и пользователям принять следующие меры:
- Если вы используете .NET 8 или более позднюю версию, установите обновление .NET из Центра обновления Microsoft, затем перезапустите приложение или перезагрузите компьютер.
- Если вы используете .NET 2.3, обновите ссылку на пакет Microsoft.AspNet.Server.Kestrel.Core до версии 2.3.6, затем перекомпилируйте и повторно разверните приложение.
- Если вы используете автономное/однофайловое приложение, установите обновление .NET, перекомпилируйте и повторно разверните.
Как пояснил руководитель технической программы безопасности .NET Барри Дорранс, последствия атак CVE-2025-55315 будут зависеть от целевого приложения ASP.NET, и успешная эксплуатация уязвимости может позволить злоумышленникам войти в систему под другим пользователем (для повышения привилегий), выполнить внутренний запрос (при атаках с подделкой запросов на стороне сервера), обойти проверки на подделку межсайтовых запросов (CSRF) или выполнить атаки с использованием инъекций.
«Мы не знаем, что из этого возможно, потому что всё зависит от того, как вы написали своё приложение. Поэтому мы оцениваем уровень риска, имея в виду наихудший возможный случай — обход функции безопасности, который позволяет изменить привилегии, — сказал Дорранс. — Вероятно ли это? Скорее всего, нет, если только код вашего приложения не делает что-то странное и не пропускает ряд проверок, которые он должен выполнять при каждом запросе. Тем не менее, пожалуйста, обновитесь».
Источник: https://www.bleepingcomputer.com/news/microsoft/microsoft-fixes-highest-severity-aspnet-core-flaw-ever/
👍11