День 2513. #ЗаметкиНаПолях
Не Используйте JSON-Сериализатор Напрямую в Коде. Начало
Вероятно, в большинстве приложений вам придётся работать с JSON. Запросы/ответы или настройки приложения — вам всё равно придётся обрабатывать JSON.
И System.Text.Json, и Newtonsoft.Json предоставляют статические классы для сериализации и десериализации, поэтому очень заманчиво использовать их как есть:
Однако использование этих статических классов приводит к ряду проблем, таких как:
- Жёсткая связь с реализацией. Если позже вы захотите перейти с System.Text.Json на Newtonsoft.Json или настроить поведение, это может привести к значительному рефакторингу;
- Отсутствие внедрения зависимостей. Гораздо удобнее, когда класс явно объявляет свои зависимости через конструктор. Со статическими сериализаторами это неочевидно или даже невозможно;
- Сложность модульного тестирования. Если класс имеет жёсткую зависимость от статического сериализатора, его сложно изолировать и использовать в качестве заглушки во время тестирования.
Поэтому существуют некоторые подходы, помогающие избежать этих проблем. Один из них — создать абстракцию над сериализатором и использовать её единообразно во всей системе:
Для более удобного использования можно создать обобщённый метод расширения:
Теперь регистрируем сериализатор в DI-контейнере и используем его там, где нам нужно, как в этом примере:
Окончание следует…
Источник: https://medium.com/@denmaklucky/dont-use-a-json-serializer-directly-in-your-code-instead-do-this-72f8eff3fdc5
Не Используйте JSON-Сериализатор Напрямую в Коде. Начало
Вероятно, в большинстве приложений вам придётся работать с JSON. Запросы/ответы или настройки приложения — вам всё равно придётся обрабатывать JSON.
И System.Text.Json, и Newtonsoft.Json предоставляют статические классы для сериализации и десериализации, поэтому очень заманчиво использовать их как есть:
using System.Text.Json;
Person person = new();
var json = JsonSerializer.Serialize(person);
Однако использование этих статических классов приводит к ряду проблем, таких как:
- Жёсткая связь с реализацией. Если позже вы захотите перейти с System.Text.Json на Newtonsoft.Json или настроить поведение, это может привести к значительному рефакторингу;
- Отсутствие внедрения зависимостей. Гораздо удобнее, когда класс явно объявляет свои зависимости через конструктор. Со статическими сериализаторами это неочевидно или даже невозможно;
- Сложность модульного тестирования. Если класс имеет жёсткую зависимость от статического сериализатора, его сложно изолировать и использовать в качестве заглушки во время тестирования.
Поэтому существуют некоторые подходы, помогающие избежать этих проблем. Один из них — создать абстракцию над сериализатором и использовать её единообразно во всей системе:
interface ISerializer
{
Task<string> SerializeAsync(object obj, Type type);
Task<object> DeserializeAsync(string json, Type type);
}
Для более удобного использования можно создать обобщённый метод расширения:
static class SerializerExtensions
{
extension(ISerializer serializer)
{
public Task<string>
SerializeAsync<TValue>(TValue value)
{
return serializer
.SerializeAsync(value, typeof(TValue));
}
public async Task<TValue>
DeserializeAsync(string json)
{
return (TValue)await serializer
.DeserializeAsync(
json,
typeof(TValue));
}
}
}
Теперь регистрируем сериализатор в DI-контейнере и используем его там, где нам нужно, как в этом примере:
class Handler(ISerializer serializer)
{
public Task<Result> InvokeAsync(Command command)
{
return serializer.DeserializeAsync(command.Json);
}
}
Окончание следует…
Источник: https://medium.com/@denmaklucky/dont-use-a-json-serializer-directly-in-your-code-instead-do-this-72f8eff3fdc5
1👎23👍13
День 2514. #ЗаметкиНаПолях
Не Используйте JSON-Сериализатор Напрямую в Коде. Окончание
Начало
Но что, если вы не можете внедрить ISerializer?
В этом случае вы можете зарегистрировать JsonSerializerOptions в DI-контейнере и использовать их напрямую:
Даже если у вас нет специфических настроек, лучше внедрить JsonSerializerOptions, задать свойства по умолчанию и использовать их везде, где требуется сериализация данных. Это важно, потому что библиотека может измениться, и её настройки по умолчанию также могут измениться. Если вы полагаетесь на настройки по умолчанию библиотеки, после обновления вы можете столкнуться с неожиданным поведением. Например, одно из обновлений Newtonsoft.Json изменило настройку по умолчанию о включении в JSON свойств со значением null. В предыдущей версии они включались, а в новой перестали.
Но в некоторых случаях и этого может быть недостаточно.
Например, при реализации пользовательского IResult путем расширения Results.Extensions нет возможности внедрить ни ISerializer, ни JsonSerializerOptions.
Тогда можно создать статический вспомогательный класс и использовать его.
Регистрация:
Использование:
Источник: https://medium.com/@denmaklucky/dont-use-a-json-serializer-directly-in-your-code-instead-do-this-72f8eff3fdc5
Не Используйте JSON-Сериализатор Напрямую в Коде. Окончание
Начало
Но что, если вы не можете внедрить ISerializer?
В этом случае вы можете зарегистрировать JsonSerializerOptions в DI-контейнере и использовать их напрямую:
using System.Text.Json;
var builder = WebApplication.CreateBuilder();
// добавьте сюда ваши настройки по умолчанию
var jsonSerializerOptions =
new JsonSerializerOptions(…);
builder.Services.AddSingleton<JsonSerializerOptions>(
jsonSerializerOptions);
Даже если у вас нет специфических настроек, лучше внедрить JsonSerializerOptions, задать свойства по умолчанию и использовать их везде, где требуется сериализация данных. Это важно, потому что библиотека может измениться, и её настройки по умолчанию также могут измениться. Если вы полагаетесь на настройки по умолчанию библиотеки, после обновления вы можете столкнуться с неожиданным поведением. Например, одно из обновлений Newtonsoft.Json изменило настройку по умолчанию о включении в JSON свойств со значением null. В предыдущей версии они включались, а в новой перестали.
Но в некоторых случаях и этого может быть недостаточно.
Например, при реализации пользовательского IResult путем расширения Results.Extensions нет возможности внедрить ни ISerializer, ни JsonSerializerOptions.
Тогда можно создать статический вспомогательный класс и использовать его.
Регистрация:
csharp
using System.Text.Json;
var builder = WebApplication.CreateBuilder();
var jsonSerializerOptions
= new JsonSerializerOptions(…);
JsonSerializerOptionsProvider.Options
= jsonSerializerOptions;
builder.Services.AddSingleton<JsonSerializerOptions>(jsonSerializerOptions);
static class JsonSerializerOptionsProvider
{
public static JsonSerializerOptions
Options { get; set; }
}
Использование:
csharp
public sealed class ErrorResult<TError>(TError error) : IResult
{
public Task ExecuteAsync(HttpContext ctx)
{
ctx.Response.StatusCode =
(int)HttpStatusCode.BadRequest;
return ctx.Response.WriteAsJsonAsync(
error,
JsonSerializerOptionsProvider.Options);
}
}
Источник: https://medium.com/@denmaklucky/dont-use-a-json-serializer-directly-in-your-code-instead-do-this-72f8eff3fdc5
👍3👎1
День 2515. #Карьера
Топ Советов по Повышению Продуктивности. Часть 1
Неприятная правда, о которой большинство разработчиков не говорят: мы годами учимся программировать, но почти не тратим времени на то, чтобы учиться работать. Мы оптимизируем алгоритмы, но не наше время. Мы переписываем код, но не изменяем привычки. Мы спорим, табы или пробелы, но не можем объяснить, почему чувствуем себя измотанными после «продуктивного» дня, когда почему-то не выпустили ничего значимого.
Это не очередной шаблонный список советов по повышению продуктивности, полный рекомендаций типа «просто больше сосредотачивайтесь». Это проверенные на практике стратегии, учитывающие уникальный хаос разработки ПО — переключение контекста, рабочие дни, полные прерываний, сеансы отладки, и постоянное давление, заставляющее изучать новые фреймворки, одновременно выпуская функции, которые нужны вчера.
1. Правило двух терминалов: прекратите переключаться между задачами
Представьте, что вы отлаживаете проблему в продакшене. Нужно проверить логи, поэтому вы переходите в каталог logs. Затем нужно перезапустить сервис, поэтому вы переходите в каталог приложения. Затем нужно выполнить запрос к БД, поэтому вы… подождите, на чём мы остановились? Вы тыкаете стрелку вверх, пытаясь найти команду, которую выполняли 5 минут назад, и теперь вы полностью потеряли нить рассуждений.
Почему это важно: Переключение между задачами — это не просто перепрыгивание между ними, это также микропереключатели, которые мы сами себе создаём, заставляя наши инструменты работать против нас. Каждый раз, когда вы отходите от того места, где вам нужно быть, вы создаёте крошечную ментальную преграду. Сделайте это десяток раз, и вы построите себе когнитивную полосу препятствий.
Решение до смешного простое: всегда работайте как минимум с двумя окнами терминала/IDE (или разделёнными панелями), каждое из которых предназначено для определённого контекста.
Например
Терминал 1 (слева) - «рабочий». Он находится в корневой директории проекта. Здесь выполняются команды сборки, запускаются тесты и выполняется прочая основная работа.
Терминал 2 (справа) - «исследовательский». Это черновик, где можно перемещаться между каталогами логов, конфигурации, репозиториями, выполнять разовые команды, проверять системные ресурсы, искать информацию в файлах. Этот терминал загрязняется, и это нормально.
Магия происходит в разделении. Терминал 1 остается чистым и предсказуемым. Всегда понятно, где он, что он запускает и что происходит, при нажатии стрелки вверх. Терминал 2 поглощает весь хаос исследования и изучения, не загрязняя основное рабочее пространство.
Развивайте эту идею дальше. Используйте один терминал для выполнения команд, другой — для мониторинга вывода. Откройте третий терминал исключительно для операций с Git. Многие IDE/терминалы позволяют:
- создавать именованные сессии для разных проектов;
- разделять панели для связанных задач (сервер сверху, наблюдатель за файлами снизу);
- использовать разные цветовые схемы (например, для соединений с производственной базой и базой для разработки – см. картинку ниже), чтобы случайно не выполнить команду в неверной БД.
В микросервисном проекте держите отдельные терминалы для каждого сервиса, плюс один для команд Docker и один для исследовательской работы. Это кажется излишним, пока вы не почувствуете состояние потока, когда не приходится переходить в другие директории или терять историю команд.
Ключевой вывод: ваши инструменты должны соответствовать вашей ментальной модели работы. Если вы думаете о нескольких контекстах одновременно, ваше рабочее пространство должно это отражать.
Источник: https://dev.to/thebitforge/top-10-productivity-hacks-every-developer-should-know-151h
Топ Советов по Повышению Продуктивности. Часть 1
Неприятная правда, о которой большинство разработчиков не говорят: мы годами учимся программировать, но почти не тратим времени на то, чтобы учиться работать. Мы оптимизируем алгоритмы, но не наше время. Мы переписываем код, но не изменяем привычки. Мы спорим, табы или пробелы, но не можем объяснить, почему чувствуем себя измотанными после «продуктивного» дня, когда почему-то не выпустили ничего значимого.
Это не очередной шаблонный список советов по повышению продуктивности, полный рекомендаций типа «просто больше сосредотачивайтесь». Это проверенные на практике стратегии, учитывающие уникальный хаос разработки ПО — переключение контекста, рабочие дни, полные прерываний, сеансы отладки, и постоянное давление, заставляющее изучать новые фреймворки, одновременно выпуская функции, которые нужны вчера.
1. Правило двух терминалов: прекратите переключаться между задачами
Представьте, что вы отлаживаете проблему в продакшене. Нужно проверить логи, поэтому вы переходите в каталог logs. Затем нужно перезапустить сервис, поэтому вы переходите в каталог приложения. Затем нужно выполнить запрос к БД, поэтому вы… подождите, на чём мы остановились? Вы тыкаете стрелку вверх, пытаясь найти команду, которую выполняли 5 минут назад, и теперь вы полностью потеряли нить рассуждений.
Почему это важно: Переключение между задачами — это не просто перепрыгивание между ними, это также микропереключатели, которые мы сами себе создаём, заставляя наши инструменты работать против нас. Каждый раз, когда вы отходите от того места, где вам нужно быть, вы создаёте крошечную ментальную преграду. Сделайте это десяток раз, и вы построите себе когнитивную полосу препятствий.
Решение до смешного простое: всегда работайте как минимум с двумя окнами терминала/IDE (или разделёнными панелями), каждое из которых предназначено для определённого контекста.
Например
Терминал 1 (слева) - «рабочий». Он находится в корневой директории проекта. Здесь выполняются команды сборки, запускаются тесты и выполняется прочая основная работа.
Терминал 2 (справа) - «исследовательский». Это черновик, где можно перемещаться между каталогами логов, конфигурации, репозиториями, выполнять разовые команды, проверять системные ресурсы, искать информацию в файлах. Этот терминал загрязняется, и это нормально.
Магия происходит в разделении. Терминал 1 остается чистым и предсказуемым. Всегда понятно, где он, что он запускает и что происходит, при нажатии стрелки вверх. Терминал 2 поглощает весь хаос исследования и изучения, не загрязняя основное рабочее пространство.
Развивайте эту идею дальше. Используйте один терминал для выполнения команд, другой — для мониторинга вывода. Откройте третий терминал исключительно для операций с Git. Многие IDE/терминалы позволяют:
- создавать именованные сессии для разных проектов;
- разделять панели для связанных задач (сервер сверху, наблюдатель за файлами снизу);
- использовать разные цветовые схемы (например, для соединений с производственной базой и базой для разработки – см. картинку ниже), чтобы случайно не выполнить команду в неверной БД.
В микросервисном проекте держите отдельные терминалы для каждого сервиса, плюс один для команд Docker и один для исследовательской работы. Это кажется излишним, пока вы не почувствуете состояние потока, когда не приходится переходить в другие директории или терять историю команд.
Ключевой вывод: ваши инструменты должны соответствовать вашей ментальной модели работы. Если вы думаете о нескольких контекстах одновременно, ваше рабочее пространство должно это отражать.
Источник: https://dev.to/thebitforge/top-10-productivity-hacks-every-developer-should-know-151h
👍4
День 2516. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
13. .NET Standard
«Объясните назначение .NET Standard и давайте обсудим его актуальность в свете появления современных версий .NET. Как .NET Standard влияет на совместное использование кода на разных платформах?»
Хороший ответ
NET Standard был разработан для обеспечения единообразия между различными реализациями .NET, такими как .NET Framework, .NET Core и Mono. Он определяет набор API, которые все реализации .NET поддерживают, гарантируя, что библиотеки, написанные для определённой версии .NET Standard, могут работать на любой реализации .NET, поддерживающей эту версию.
С выпуском .NET 5 и его преемников, которые объединяют платформу .NET в единый фреймворк, роль .NET Standard меняется. .NET 5 и более поздние версии стремятся объединить лучшие возможности .NET Core, .NET Framework и Mono в единую платформу, которая поддерживает создание приложений для любой операционной системы без необходимости использования разных кодовых баз. В этом контексте .NET Standard по-прежнему служит ценным инструментом для библиотек, которым необходимо поддерживать совместимость со старыми платформами, такими как .NET Framework или более ранние версии .NET Core, которые не являются совместимыми с .NET 5+.
Например, если разработчику необходимо создать библиотеку, которая будет использоваться как в .NET Framework 4.8, так и в .NET 8+, ориентация на .NET Standard 2.0 или 2.1 может по-прежнему оставаться лучшим вариантом для обеспечения совместимости на этих платформах. Однако для новых проектов, ориентированных только на современные реализации .NET, рекомендуется прямая ориентация на эти более новые платформы, поскольку это позволяет разработчикам в полной мере использовать последние возможности и улучшения.
.NET Standard остаётся актуальным для поддержки устаревших систем и обеспечения связи старых библиотек с более новыми средами, но его важность, вероятно, будет уменьшаться по мере развития экосистемы и перехода всё большего числа систем на последние версии .NET.
Часто встречающийся неверный ответ
«NET Standard больше не нужен, потому что .NET 8 заменяет все старые версии, такие как .NET Core и .NET Framework, и работает одинаково на всех платформах».
Этот ответ неверно понимает переход к современному .NET и сохраняющуюся роль .NET Standard:
- Неправильное толкование области применения .NET 5+: Хотя .NET 5 и более поздние версии объединяют различные реализации .NET, они не заменяют автоматически .NET Framework или более ранние версии .NET Core во всех средах, особенно в устаревших системах, которые всё ещё используются.
- Игнорирование совместимости с устаревшими системами: В ответе упускается из виду важность .NET Standard для поддержания совместимости со старыми платформами и обеспечения возможности использования существующих библиотек в новых проектах. .NET Standard помогает управлять переходным периодом, когда в рамках одного проекта или организации существуют приложения, ориентированные на разные платформы.
- Упрощение различий платформ: Упускается из виду, что, хотя .NET 5 и более поздние версии предоставляют единую платформу, различия в API между различными версиями фреймворка всё ещё требуют внимания, особенно при работе с устаревшими системами или библиотеками, ориентированными на несколько версий.
Эта ошибка обычно возникает из-за недостаточного понимания того, как взаимодействуют и развиваются платформы и стандарты .NET, что приводит к упрощённому представлению о технологическом ландшафте.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
13. .NET Standard
«Объясните назначение .NET Standard и давайте обсудим его актуальность в свете появления современных версий .NET. Как .NET Standard влияет на совместное использование кода на разных платформах?»
Хороший ответ
NET Standard был разработан для обеспечения единообразия между различными реализациями .NET, такими как .NET Framework, .NET Core и Mono. Он определяет набор API, которые все реализации .NET поддерживают, гарантируя, что библиотеки, написанные для определённой версии .NET Standard, могут работать на любой реализации .NET, поддерживающей эту версию.
С выпуском .NET 5 и его преемников, которые объединяют платформу .NET в единый фреймворк, роль .NET Standard меняется. .NET 5 и более поздние версии стремятся объединить лучшие возможности .NET Core, .NET Framework и Mono в единую платформу, которая поддерживает создание приложений для любой операционной системы без необходимости использования разных кодовых баз. В этом контексте .NET Standard по-прежнему служит ценным инструментом для библиотек, которым необходимо поддерживать совместимость со старыми платформами, такими как .NET Framework или более ранние версии .NET Core, которые не являются совместимыми с .NET 5+.
Например, если разработчику необходимо создать библиотеку, которая будет использоваться как в .NET Framework 4.8, так и в .NET 8+, ориентация на .NET Standard 2.0 или 2.1 может по-прежнему оставаться лучшим вариантом для обеспечения совместимости на этих платформах. Однако для новых проектов, ориентированных только на современные реализации .NET, рекомендуется прямая ориентация на эти более новые платформы, поскольку это позволяет разработчикам в полной мере использовать последние возможности и улучшения.
.NET Standard остаётся актуальным для поддержки устаревших систем и обеспечения связи старых библиотек с более новыми средами, но его важность, вероятно, будет уменьшаться по мере развития экосистемы и перехода всё большего числа систем на последние версии .NET.
Часто встречающийся неверный ответ
«NET Standard больше не нужен, потому что .NET 8 заменяет все старые версии, такие как .NET Core и .NET Framework, и работает одинаково на всех платформах».
Этот ответ неверно понимает переход к современному .NET и сохраняющуюся роль .NET Standard:
- Неправильное толкование области применения .NET 5+: Хотя .NET 5 и более поздние версии объединяют различные реализации .NET, они не заменяют автоматически .NET Framework или более ранние версии .NET Core во всех средах, особенно в устаревших системах, которые всё ещё используются.
- Игнорирование совместимости с устаревшими системами: В ответе упускается из виду важность .NET Standard для поддержания совместимости со старыми платформами и обеспечения возможности использования существующих библиотек в новых проектах. .NET Standard помогает управлять переходным периодом, когда в рамках одного проекта или организации существуют приложения, ориентированные на разные платформы.
- Упрощение различий платформ: Упускается из виду, что, хотя .NET 5 и более поздние версии предоставляют единую платформу, различия в API между различными версиями фреймворка всё ещё требуют внимания, особенно при работе с устаревшими системами или библиотеками, ориентированными на несколько версий.
Эта ошибка обычно возникает из-за недостаточного понимания того, как взаимодействуют и развиваются платформы и стандарты .NET, что приводит к упрощённому представлению о технологическом ландшафте.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👍11
Для материализации результатов из IQueryable в Entity Framework вы используете…
Anonymous Poll
25%
ToArrayAsync()
75%
ToListAsync()
👍4👎4
День 2517. #TipsAndTricks
ToArrayAsync или ToListAsync в Entity Framework?
Очевидно, каждый из методов нужно использовать в подходящей для него ситуации. Но если это не важно, какой лучше? Короткий ответ: ToListAsync.
Посмотрим код. ToListAsync реализован следующим образом:
См. источник
А ToArrayAsync реализован так:
См. источник
Таким образом, ToArrayAsync сначала вызывает ToListAsync, а затем преобразует полученный список в массив с помощью ToArray(). В результате возникают накладные расходы на создание списка, его заполнение, а затем создание массива и копирование элементов.
Это не относится к ToHashSetAsync, он реализован аналогично ToListAsync.
Источник: https://steven-giesel.com/blogPost/55dbb67c-cd14-444f-81e8-dec9f5ce1448/one-minute-knowledge-is-toarrayasync-or-tolistasync-faster-for-entity-framework
ToArrayAsync или ToListAsync в Entity Framework?
Очевидно, каждый из методов нужно использовать в подходящей для него ситуации. Но если это не важно, какой лучше? Короткий ответ: ToListAsync.
Посмотрим код. ToListAsync реализован следующим образом:
public static async Task<List<TSource>>
ToListAsync<TSource>(
this IQueryable<TSource> source,
CancellationToken cancellationToken = default)
{
var list = new List<TSource>();
await foreach (var element in
source
.AsAsyncEnumerable()
.WithCancellation(cancellationToken)
.ConfigureAwait(false))
{
list.Add(element);
}
return list;
}
См. источник
А ToArrayAsync реализован так:
public static async Task<TSource[]>
ToArrayAsync<TSource>(
this IQueryable<TSource> source,
CancellationToken cancellationToken = default)
=> (
await source
.ToListAsync(cancellationToken)
.ConfigureAwait(false))
.ToArray();
См. источник
Таким образом, ToArrayAsync сначала вызывает ToListAsync, а затем преобразует полученный список в массив с помощью ToArray(). В результате возникают накладные расходы на создание списка, его заполнение, а затем создание массива и копирование элементов.
Это не относится к ToHashSetAsync, он реализован аналогично ToListAsync.
Источник: https://steven-giesel.com/blogPost/55dbb67c-cd14-444f-81e8-dec9f5ce1448/one-minute-knowledge-is-toarrayasync-or-tolistasync-faster-for-entity-framework
1👍23
День 2518. #ЗаметкиНаПолях
Мигрируем на Новый Формат Файлов Решений
Новый формат файлов решений в .NET .slnx появился довольно давно, я уже писал про него. И хотя он до сих пор, по какой-то причине находится в стадии превью, уже сейчас можно обновлять свои решения на новый формат.
Проблема
Классические файлы .sln многословны: записи проекта с большим количеством GUID + блоки конфигурации, которые разрастаются по мере роста вашего решения. Это также частый источник конфликтов слияния.
Новый формат .slnx по сути просто перечисляет проекты решения в формате XML. Он похож на файл .csproj. Гораздо компактнее, понятнее и удобнее в использовании.
Как выполнить миграцию?
Формат .slnx доступен в последних версиях Visual Studio 2022 (v17.13+) и SDK .NET9+. Вот как вы можете перейти на него.
1. Терминал
Если у вас установлен SDK .NET версии 9.0.200 или более поздней, вы можете выполнить миграцию мгновенно через CLI.
- Откройте терминал в папке решения.
- Выполните команду миграции:
Это создаст новый файл .slnx рядом со старым файлом .sln.
На этом этапе лучше удалить старый файл .sln, чтобы избежать путаницы. Нет смысла хранить оба файла в одном репозитории.
2. Сохранение из Visual Studio
Если вы предпочитаете графический интерфейс, вы можете сделать это непосредственно в Visual Studio 2022 (или 2026).
- Выберите решение в обозревателе решений.
- Перейдите в меню File > Save Solution As… (Файл > Сохранить решение как…).
- Выберите в раскрывающемся списке Save as type (Тип файла) тип Xml Solution File (*.slnx).
Зачем мигрировать?
1. Меньше конфликтов слияния: главное преимущество. Поскольку файл представляет собой простой XML-файл без случайных изменяющихся GUID, слияние в Git становится тривиальным.
2. Удобочитаемость: Вы можете открыть этот файл в блокноте, понять его и отредактировать, не нарушая всю сборку.
3. Согласованность: Наконец-то формат решения приведён в соответствие с форматом проекта (.csproj), который несколько лет назад также был серьёзно упрощён.
4. Производительность: Меньший размер файлов и более простой анализ означают немного более быструю загрузку для масштабных решений.
Можно ли использовать в проде?
По состоянию на конец 2025 - начало 2026 года, .slnx технически остаётся превью-функцией.
Безопасно ли использовать?
Да, формат стабилен.
Поддержка инструментов
Visual Studio 2022, Visual Studio 2026 и Rider хорошо его поддерживают. То же самое относится и к .NET CLI. Некоторые старые конвейеры CI/CD или сторонние инструменты могут пока не распознавать это расширение. Сначала попробуйте его на стороннем проекте или ветке. Если ваш конвейер CI/CD успешно будет с ним работать, то всё отлично.
Источник: https://www.milanjovanovic.tech/blog/the-new-slnx-solution-format-migration-guide
Мигрируем на Новый Формат Файлов Решений
Новый формат файлов решений в .NET .slnx появился довольно давно, я уже писал про него. И хотя он до сих пор, по какой-то причине находится в стадии превью, уже сейчас можно обновлять свои решения на новый формат.
Проблема
Классические файлы .sln многословны: записи проекта с большим количеством GUID + блоки конфигурации, которые разрастаются по мере роста вашего решения. Это также частый источник конфликтов слияния.
Новый формат .slnx по сути просто перечисляет проекты решения в формате XML. Он похож на файл .csproj. Гораздо компактнее, понятнее и удобнее в использовании.
Как выполнить миграцию?
Формат .slnx доступен в последних версиях Visual Studio 2022 (v17.13+) и SDK .NET9+. Вот как вы можете перейти на него.
1. Терминал
Если у вас установлен SDK .NET версии 9.0.200 или более поздней, вы можете выполнить миграцию мгновенно через CLI.
- Откройте терминал в папке решения.
- Выполните команду миграции:
dotnet sln migrate
Это создаст новый файл .slnx рядом со старым файлом .sln.
На этом этапе лучше удалить старый файл .sln, чтобы избежать путаницы. Нет смысла хранить оба файла в одном репозитории.
2. Сохранение из Visual Studio
Если вы предпочитаете графический интерфейс, вы можете сделать это непосредственно в Visual Studio 2022 (или 2026).
- Выберите решение в обозревателе решений.
- Перейдите в меню File > Save Solution As… (Файл > Сохранить решение как…).
- Выберите в раскрывающемся списке Save as type (Тип файла) тип Xml Solution File (*.slnx).
Зачем мигрировать?
1. Меньше конфликтов слияния: главное преимущество. Поскольку файл представляет собой простой XML-файл без случайных изменяющихся GUID, слияние в Git становится тривиальным.
2. Удобочитаемость: Вы можете открыть этот файл в блокноте, понять его и отредактировать, не нарушая всю сборку.
3. Согласованность: Наконец-то формат решения приведён в соответствие с форматом проекта (.csproj), который несколько лет назад также был серьёзно упрощён.
4. Производительность: Меньший размер файлов и более простой анализ означают немного более быструю загрузку для масштабных решений.
Можно ли использовать в проде?
По состоянию на конец 2025 - начало 2026 года, .slnx технически остаётся превью-функцией.
Безопасно ли использовать?
Да, формат стабилен.
Поддержка инструментов
Visual Studio 2022, Visual Studio 2026 и Rider хорошо его поддерживают. То же самое относится и к .NET CLI. Некоторые старые конвейеры CI/CD или сторонние инструменты могут пока не распознавать это расширение. Сначала попробуйте его на стороннем проекте или ветке. Если ваш конвейер CI/CD успешно будет с ним работать, то всё отлично.
Источник: https://www.milanjovanovic.tech/blog/the-new-slnx-solution-format-migration-guide
👍13
День 2519. #ЗаметкиНаПолях
Разбираем Server-Sent Events в ASP.NET Core и .NET 10. Начало
Обновления UI в реальном времени больше не являются "желательной" функцией. Большинство современных приложений ожидают потоков данных в реальном времени от сервера. В течение многих лет основным решением в экосистеме .NET был SignalR. Хотя он невероятно мощный, приятно иметь другие варианты для более простых сценариев использования.
В ASP.NET Core 10 появился собственный высокоуровневый API для событий, отправляемых сервером (SSE). Он устраняет разрыв между базовым HTTP-опросом и полнодуплексными WebSockets через SignalR.
Зачем?
SignalR — мощный инструмент, который автоматически обрабатывает WebSockets, Long Polling и SSE, обеспечивая полнодуплексный (двусторонний) канал связи. Однако это сопряжено с определёнными затратами: специфический протокол, необходимость в клиентской библиотеке и потребность в «липких сессиях» или бэкэнде (например, Redis) для масштабирования.
В отличие от SignalR, SSE:
- Однонаправленные - разработаны специально для потоковой передачи данных с сервера на клиент.
- Нативны для HTTP - это стандартный HTTP-запрос с типом содержимого text/event-stream. Никаких пользовательских протоколов.
- Поддерживают автоматическое переподключение - браузеры обрабатывают переподключения напрямую через API EventSource.
- Легковесны - нет тяжёлых клиентских библиотек или сложной логики рукопожатия.
Простейшая конечная точка SSE
Мы можем использовать новый объект Results.ServerSentEvents для возврата потока событий из любого IAsyncEnumerable<T>. Поскольку IAsyncEnumerable представляет собой поток данных, который может поступать с течением времени, сервер знает, что нужно поддерживать HTTP-соединение открытым, а не закрывать его после первого «фрагмента» данных.
Вот минимальный пример конечной точки SSE, которая передаёт информацию о размещении заказов в режиме реального времени:
Когда клиент обращается к этой конечной точке:
- Сервер отправляет заголовок
- Соединение остается активным в состоянии ожидания данных.
- Как только приложение отправляет заказ в канал, IAsyncEnumerable возвращает этот элемент, и .NET немедленно отправляет его по открытому HTTP-каналу в браузер.
Это невероятно эффективный способ обработки «push»-уведомлений без накладных расходов, связанных с протоколом с сохранением состояния.
В этом примере используется канал. В реальном приложении у вас может быть фоновый сервис, прослушивающий очередь сообщений (например, RabbitMQ или Azure Service Bus) или канал изменений БД и отправляющий новые события в канал для обработки подключёнными клиентами.
Продолжение следует…
Источник: https://www.milanjovanovic.tech/blog/server-sent-events-in-aspnetcore-and-dotnet-10
Разбираем Server-Sent Events в ASP.NET Core и .NET 10. Начало
Обновления UI в реальном времени больше не являются "желательной" функцией. Большинство современных приложений ожидают потоков данных в реальном времени от сервера. В течение многих лет основным решением в экосистеме .NET был SignalR. Хотя он невероятно мощный, приятно иметь другие варианты для более простых сценариев использования.
В ASP.NET Core 10 появился собственный высокоуровневый API для событий, отправляемых сервером (SSE). Он устраняет разрыв между базовым HTTP-опросом и полнодуплексными WebSockets через SignalR.
Зачем?
SignalR — мощный инструмент, который автоматически обрабатывает WebSockets, Long Polling и SSE, обеспечивая полнодуплексный (двусторонний) канал связи. Однако это сопряжено с определёнными затратами: специфический протокол, необходимость в клиентской библиотеке и потребность в «липких сессиях» или бэкэнде (например, Redis) для масштабирования.
В отличие от SignalR, SSE:
- Однонаправленные - разработаны специально для потоковой передачи данных с сервера на клиент.
- Нативны для HTTP - это стандартный HTTP-запрос с типом содержимого text/event-stream. Никаких пользовательских протоколов.
- Поддерживают автоматическое переподключение - браузеры обрабатывают переподключения напрямую через API EventSource.
- Легковесны - нет тяжёлых клиентских библиотек или сложной логики рукопожатия.
Простейшая конечная точка SSE
Мы можем использовать новый объект Results.ServerSentEvents для возврата потока событий из любого IAsyncEnumerable<T>. Поскольку IAsyncEnumerable представляет собой поток данных, который может поступать с течением времени, сервер знает, что нужно поддерживать HTTP-соединение открытым, а не закрывать его после первого «фрагмента» данных.
Вот минимальный пример конечной точки SSE, которая передаёт информацию о размещении заказов в режиме реального времени:
app.MapGet("orders/realtime", (
ChannelReader<OrderPlacement> reader,
CancellationToken ct) =>
{
// ReadAllAsync возвращает IAsyncEnumerable
// Results.ServerSentEvents заставляет браузер держать подключение открытым
// Новые данные передаются клиенту по мере поступления из канала
return Results.ServerSentEvents(
reader.ReadAllAsync(ct),
eventType: "orders");
});Когда клиент обращается к этой конечной точке:
- Сервер отправляет заголовок
Content-Type: text/event-stream.- Соединение остается активным в состоянии ожидания данных.
- Как только приложение отправляет заказ в канал, IAsyncEnumerable возвращает этот элемент, и .NET немедленно отправляет его по открытому HTTP-каналу в браузер.
Это невероятно эффективный способ обработки «push»-уведомлений без накладных расходов, связанных с протоколом с сохранением состояния.
В этом примере используется канал. В реальном приложении у вас может быть фоновый сервис, прослушивающий очередь сообщений (например, RabbitMQ или Azure Service Bus) или канал изменений БД и отправляющий новые события в канал для обработки подключёнными клиентами.
Продолжение следует…
Источник: https://www.milanjovanovic.tech/blog/server-sent-events-in-aspnetcore-and-dotnet-10
👍22
День 2520. #ЗаметкиНаПолях
Разбираем Server-Sent Events в ASP.NET Core и .NET 10. Продолжение
Начало
Обработка пропущенных событий
У простой конечной точки, которую мы создали ранее, есть один недостаток: ей не хватает отказоустойчивости.
Одна из самых больших проблем с потоками в реальном времени — это разрывы соединения. К тому времени, когда браузер автоматически переподключится, несколько событий могут быть уже отправлены и потеряны. Для решения этой проблемы в SSE есть встроенный механизм: заголовок Last-Event-ID. Когда браузер переподключается, он отправляет этот ID обратно на сервер.
В .NET 10 мы можем использовать тип SseItem<T> для добавления метаданных к нашим данным (идентификаторы и интервалы повторных попыток).
Можно создать простой буфер OrderEventBuffer в памяти, содержащий объекты типа SseItem<OrderPlacement>, и методом получения всех объектов после Last-Event-ID, который будет предоставляться браузером. Так мы можем «воспроизвести» пропущенные сообщения при переподключении:
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/server-sent-events-in-aspnetcore-and-dotnet-10
Разбираем Server-Sent Events в ASP.NET Core и .NET 10. Продолжение
Начало
Обработка пропущенных событий
У простой конечной точки, которую мы создали ранее, есть один недостаток: ей не хватает отказоустойчивости.
Одна из самых больших проблем с потоками в реальном времени — это разрывы соединения. К тому времени, когда браузер автоматически переподключится, несколько событий могут быть уже отправлены и потеряны. Для решения этой проблемы в SSE есть встроенный механизм: заголовок Last-Event-ID. Когда браузер переподключается, он отправляет этот ID обратно на сервер.
В .NET 10 мы можем использовать тип SseItem<T> для добавления метаданных к нашим данным (идентификаторы и интервалы повторных попыток).
Можно создать простой буфер OrderEventBuffer в памяти, содержащий объекты типа SseItem<OrderPlacement>, и методом получения всех объектов после Last-Event-ID, который будет предоставляться браузером. Так мы можем «воспроизвести» пропущенные сообщения при переподключении:
app.MapGet("orders/realtime/with-replays", (
ChannelReader<OrderPlacement> reader,
OrderEventBuffer buffer,
[FromHeader(Name = "Last-Event-ID")]
string? lastEventId,
CancellationToken ct) =>
{
async IAsyncEnumerable<SseItem<OrderPlacement>>
StreamEvents()
{
// Повторяем пропущенные события
if (!string.IsNullOrWhiteSpace(lastEventId))
{
var missed = buffer.GetEventsAfter(lastEventId);
foreach (var m in missed)
yield return m;
}
// Выдаём события по мере их поступления в канал
await foreach (var order in
reader.ReadAllAsync(ct))
{
// Буфер назначает уникальный ID
var sseItem = buffer.Add(order);
yield return sseItem;
}
}
return TypedResults.ServerSentEvents(
StreamEvents(), "orders");
});Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/server-sent-events-in-aspnetcore-and-dotnet-10
👍15
День 2521. #ЗаметкиНаПолях
Разбираем Server-Sent Events в ASP.NET Core и .NET 10. Окончание
Начало
Продолжение
Фильтрация SSE по пользователю
SSE построены на основе стандартного HTTP. Поскольку это стандартный GET-запрос, вся существующая инфраструктура «просто работает»:
- Безопасность - вы можете передать стандартный JWT в заголовке Authorization.
- Контекст пользователя - вы можете получить доступ к HttpContext.User, чтобы извлечь идентификатор пользователя и отфильтровать поток. Вы отправляете пользователю только те данные, которые ему принадлежат.
Вот пример конечной точки SSE, которая передаёт потоком только заказы для конкретного пользователя:
Обратите внимание, что при отправке сообщения в канал оно транслируется всем подключённым клиентам (ChannelReader<T>). Это вряд ли подойдёт, когда надо разделять сообщения для каждого пользователя. Поэтому в производственной среде понадобится более надёжная логика.
Обработка SSE в JavaScript
На стороне клиента не нужно устанавливать дополнительных npm-пакетов. Нативный API EventSource браузера берёт на себя основную работу, включая логику «повторного подключения и отправки Last-Event-ID», которую мы рассмотрели в предыдущем посте.
Итого
SSE в .NET 10 — это идеальный компромисс для простых односторонних обновлений, таких как панели мониторинга, уведомления и индикаторы выполнения. Они легковесны, работают по протоколу HTTP и легко обеспечивают безопасность с помощью существующего промежуточного ПО.
Однако SignalR остаётся надёжным и проверенным в боевых условиях вариантом для сложных двусторонних коммуникаций или масштабных задач, требующих бэкэнда.
Цель SSE не в замене SignalR, а в предоставлении более простого инструмента для более простых задач. Выбирайте самый простой инструмент, который решает вашу проблему.
Источник: https://www.milanjovanovic.tech/blog/server-sent-events-in-aspnetcore-and-dotnet-10
Разбираем Server-Sent Events в ASP.NET Core и .NET 10. Окончание
Начало
Продолжение
Фильтрация SSE по пользователю
SSE построены на основе стандартного HTTP. Поскольку это стандартный GET-запрос, вся существующая инфраструктура «просто работает»:
- Безопасность - вы можете передать стандартный JWT в заголовке Authorization.
- Контекст пользователя - вы можете получить доступ к HttpContext.User, чтобы извлечь идентификатор пользователя и отфильтровать поток. Вы отправляете пользователю только те данные, которые ему принадлежат.
Вот пример конечной точки SSE, которая передаёт потоком только заказы для конкретного пользователя:
app.MapGet("orders/realtime", (
ChannelReader<OrderPlacement> reader,
// Внедрённый контекст с метаданными пользователя
IUserContext context,
CancellationToken ct) =>
{
// Реализация IUserContext получает UserId из токена доступа JWT
var userId = context.UserId;
async IAsyncEnumerable<OrderPlacement>
GetUserOrders()
{
await foreach (var order in
reader.ReadAllAsync(ct))
{
// Выдаём только данные текущего пользователя
if (order.CustomerId == userId)
yield return order;
}
}
return TypedResults.ServerSentEvents(
GetUserOrders(),
"orders");
})
// Стандартная авторизация ASP.NET
.RequireAuthorization();Обратите внимание, что при отправке сообщения в канал оно транслируется всем подключённым клиентам (ChannelReader<T>). Это вряд ли подойдёт, когда надо разделять сообщения для каждого пользователя. Поэтому в производственной среде понадобится более надёжная логика.
Обработка SSE в JavaScript
На стороне клиента не нужно устанавливать дополнительных npm-пакетов. Нативный API EventSource браузера берёт на себя основную работу, включая логику «повторного подключения и отправки Last-Event-ID», которую мы рассмотрели в предыдущем посте.
const es =
new EventSource('/orders/realtime/with-replays');
// Слушаем специальный тип событий 'orders', который определён в коде C# (см. выше)
es.addEventListener('orders', (event) => {
const payload = JSON.parse(event.data);
console.log(
`Новый заказ ${event.lastEventId}:`, payload.data);
});
// При открытии подключения
es.onopen = () => {
console.log('Connection opened');
};
// Обрабатываем общие сообщения (если есть)
es.onmessage = (event) => {
console.log('Получено сообщение:', event);
};
// Обрабатываем ошибки и переподключения
es.onerror = () => {
if (es.readyState === EventSource.CONNECTING) {
console.log('Повторное подключение…');
}
};
Итого
SSE в .NET 10 — это идеальный компромисс для простых односторонних обновлений, таких как панели мониторинга, уведомления и индикаторы выполнения. Они легковесны, работают по протоколу HTTP и легко обеспечивают безопасность с помощью существующего промежуточного ПО.
Однако SignalR остаётся надёжным и проверенным в боевых условиях вариантом для сложных двусторонних коммуникаций или масштабных задач, требующих бэкэнда.
Цель SSE не в замене SignalR, а в предоставлении более простого инструмента для более простых задач. Выбирайте самый простой инструмент, который решает вашу проблему.
Источник: https://www.milanjovanovic.tech/blog/server-sent-events-in-aspnetcore-and-dotnet-10
👍6
День 2522. #Карьера
Топ Советов по Повышению Продуктивности. Часть 2
Часть 1
2. Агрессивный таймбоксинг: техника Помодоро для разработчиков (на этот раз действительно работающая)
Да, да, все уже слышали о Помодоро. 25 минут работы, 5 минут перерыва, и так до бесконечности. В теории звучит отлично, а на практике… Вы уже 11 минут сосредоточенно работаете над кодом, когда срабатывает таймер, и вы должны просто… остановиться? На полпути? Когда вы наконец-то вошли в «поток»? Да ни за что!
Тут вот в чём дело: традиционная техника Помодоро не была разработана для творческой работы, требующей глубокой концентрации. Она была создана для продаж и административных задач, где перерывы обходятся дешевле. Разработчикам нужно что-то другое.
Помодоро для разработчиков, или «агрессивный таймбоксинг».
Вместо жестких 25-минутных интервалов используйте таймбоксинг, основанный на естественных границах работы, и поддерживайте строгие правила относительно того, что происходит, когда таймер достигает нуля.
Шаг 1: Определите конкретную, выполнимую задачу. Не «работать над функцией аутентификации», а «реализовать промежуточное ПО для проверки JWT». Что-то, что вы можете закончить или промежуточный этап, на котором сможете чётко остановиться.
Шаг 2: Честно оцените время. Это займёт 30 минут? 45? 90? Будьте реалистичны. Добавьте 25% к тому, что подсказывает интуиция, потому что вы, вероятно, недооцениваете.
Шаг 3: Установите таймер и начните. Важно: не останавливайтесь, когда срабатывает таймер. Вместо этого оцените результат:
- Вы «в потоке»? Добавьте ещё один временной интервал и продолжайте.
- Застряли? Это естественная точка остановки. Сделайте перерыв.
- Слишком часто переключаетесь между задачами? Таймер вас поймал — признайте, что выбились из рабочего ритма, перефокусируйтесь или сделайте перерыв.
Шаг 4: После 2-3 последовательных временных интервалов (90-120 минут) сделайте настоящий перерыв. Не перерыв для проверки мессенджеров. Прогуляйтесь по офису или выйдите на улицу, по-настоящему отключитесь от работы.
Главное: Отслеживайте каждый временной интервал в простом документе. Записывайте, над чем вы работали и закончили ли. Это создаёт подотчетность и, что более важно, данные. Через неделю вы увидите закономерности:
- заметите, что всегда недооцениваете работу с БД на 40%;
- увидите, что дневные/вечерние временные интервалы менее продуктивны.
У вас будут реальные доказательства для улучшения планирования.
Настоящая польза: дело не в таймере или перерывах. Дело в осознании того, как вы работаете. Как только вы узнаете свои паттерны — когда вы наиболее сосредоточены, как долго можете поддерживать глубокую работу, какие задачи всегда выходят за рамки оценок — вы сможете соответствующим образом планировать свой день: планировать наиболее сложную работу на наиболее продуктивные интервалы. Это не продуктивность, основанная на мотивации; это биология. Вы работаете в соответствии со своими естественными ритмами, а не против них.
Источник: https://dev.to/thebitforge/top-10-productivity-hacks-every-developer-should-know-151h
Топ Советов по Повышению Продуктивности. Часть 2
Часть 1
2. Агрессивный таймбоксинг: техника Помодоро для разработчиков (на этот раз действительно работающая)
Да, да, все уже слышали о Помодоро. 25 минут работы, 5 минут перерыва, и так до бесконечности. В теории звучит отлично, а на практике… Вы уже 11 минут сосредоточенно работаете над кодом, когда срабатывает таймер, и вы должны просто… остановиться? На полпути? Когда вы наконец-то вошли в «поток»? Да ни за что!
Тут вот в чём дело: традиционная техника Помодоро не была разработана для творческой работы, требующей глубокой концентрации. Она была создана для продаж и административных задач, где перерывы обходятся дешевле. Разработчикам нужно что-то другое.
Помодоро для разработчиков, или «агрессивный таймбоксинг».
Вместо жестких 25-минутных интервалов используйте таймбоксинг, основанный на естественных границах работы, и поддерживайте строгие правила относительно того, что происходит, когда таймер достигает нуля.
Шаг 1: Определите конкретную, выполнимую задачу. Не «работать над функцией аутентификации», а «реализовать промежуточное ПО для проверки JWT». Что-то, что вы можете закончить или промежуточный этап, на котором сможете чётко остановиться.
Шаг 2: Честно оцените время. Это займёт 30 минут? 45? 90? Будьте реалистичны. Добавьте 25% к тому, что подсказывает интуиция, потому что вы, вероятно, недооцениваете.
Шаг 3: Установите таймер и начните. Важно: не останавливайтесь, когда срабатывает таймер. Вместо этого оцените результат:
- Вы «в потоке»? Добавьте ещё один временной интервал и продолжайте.
- Застряли? Это естественная точка остановки. Сделайте перерыв.
- Слишком часто переключаетесь между задачами? Таймер вас поймал — признайте, что выбились из рабочего ритма, перефокусируйтесь или сделайте перерыв.
Шаг 4: После 2-3 последовательных временных интервалов (90-120 минут) сделайте настоящий перерыв. Не перерыв для проверки мессенджеров. Прогуляйтесь по офису или выйдите на улицу, по-настоящему отключитесь от работы.
Главное: Отслеживайте каждый временной интервал в простом документе. Записывайте, над чем вы работали и закончили ли. Это создаёт подотчетность и, что более важно, данные. Через неделю вы увидите закономерности:
- заметите, что всегда недооцениваете работу с БД на 40%;
- увидите, что дневные/вечерние временные интервалы менее продуктивны.
У вас будут реальные доказательства для улучшения планирования.
Настоящая польза: дело не в таймере или перерывах. Дело в осознании того, как вы работаете. Как только вы узнаете свои паттерны — когда вы наиболее сосредоточены, как долго можете поддерживать глубокую работу, какие задачи всегда выходят за рамки оценок — вы сможете соответствующим образом планировать свой день: планировать наиболее сложную работу на наиболее продуктивные интервалы. Это не продуктивность, основанная на мотивации; это биология. Вы работаете в соответствии со своими естественными ритмами, а не против них.
Источник: https://dev.to/thebitforge/top-10-productivity-hacks-every-developer-should-know-151h
👍14
День 2523. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
14. Внедрение зависимостей в .NET
«Как реализовать внедрение зависимостей (DI) в приложении .NET, и каковы основные преимущества использования DI? Пожалуйста, приведите примеры кода, демонстрирующие, как настроить и использовать DI для управления сервисами и зависимостями».
Хороший ответ
Внедрение зависимостей в .NET — это метод, используемый для достижения слабой связанности между объектами и их зависимостями. Используя DI-контейнер, .NET управляет созданием и внедрением зависимостей вместо того, чтобы классы сами их создавали. Такой подход упрощает проектирование классов, повышает модульность и улучшает тестируемость приложений.
Для реализации DI в .NET обычно определяют сервисы и интерфейсы, а затем регистрируют их во встроенном DI-контейнере в файле Program.cs, как показано в следующем коде:
В этом примере IService — интерфейс, а MyService — класс, реализующий его. Сервис регистрируется как имеющий область видимости (scoped), то есть для каждого запроса будет создаваться новый экземпляр, но он будет использоваться совместно в рамках одного запроса. Другие распространённые варианты:
- transient - новый экземпляр создаётся при каждом использовании;
- singleton – создаётся один экземпляр на всё время жизни приложения.
Ключевые преимущества использования DI:
- Слабая связанность: Объекты не имеют жёстко заданных зависимостей. Это упрощает модификацию и расширение системы.
- Удобство тестирования: Зависимости можно заменять моками или заглушками во время тестирования, что упрощает написание тестов и повышает их надёжность.
- Централизованная конфигурация: Управление созданием объектов и разрешением зависимостей в одном месте делает код чище, а приложение — более масштабируемым.
Часто встречающийся неверный ответ
«В .NET достаточно добавить сервисы в файл Program.cs, используя builder.Services.AddSingleton() или любой аналогичный метод, и .NET автоматически обработает все требования DI без дополнительной настройки».
Этот ответ демонстрирует неполное понимание DI:
- Недостаток деталей о потреблении зависимостей: В ответе упоминается добавление сервисов в DI-контейнер, но не объясняется, как эти сервисы потребляются в приложении. Простая регистрация сервиса не завершает процесс DI; разработчики также должны внедрять эти сервисы в компоненты, где они необходимы.
- Чрезмерное упрощение DI: Ответ подразумевает, что DI не требует тщательного рассмотрения времени жизни сервисов или того, как зависимости внедряются в потребляющие классы. Он упускает из виду важность выбора правильного времени жизни сервиса (singleton, scoped, transient) в зависимости от варианта использования.
Такая ошибка обычно возникает, когда разработчик знает основы добавления сервисов в контейнер внедрения зависимостей, но ему не хватает более глубокого понимания того, как проектировать приложения, чтобы в полной мере использовать преимущества внедрения зависимостей.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
14. Внедрение зависимостей в .NET
«Как реализовать внедрение зависимостей (DI) в приложении .NET, и каковы основные преимущества использования DI? Пожалуйста, приведите примеры кода, демонстрирующие, как настроить и использовать DI для управления сервисами и зависимостями».
Хороший ответ
Внедрение зависимостей в .NET — это метод, используемый для достижения слабой связанности между объектами и их зависимостями. Используя DI-контейнер, .NET управляет созданием и внедрением зависимостей вместо того, чтобы классы сами их создавали. Такой подход упрощает проектирование классов, повышает модульность и улучшает тестируемость приложений.
Для реализации DI в .NET обычно определяют сервисы и интерфейсы, а затем регистрируют их во встроенном DI-контейнере в файле Program.cs, как показано в следующем коде:
var builder = WebApplication.CreateBuilder(args);
// Регистрируем Scoped-сервис
builder.Services.AddScoped<IService, MyService>();
var app = builder.Build();
// Используем сервис в компоненте приложения
app.MapGet("/", (IService service) => {
return service.PerformOperation();
});
app.Run();
В этом примере IService — интерфейс, а MyService — класс, реализующий его. Сервис регистрируется как имеющий область видимости (scoped), то есть для каждого запроса будет создаваться новый экземпляр, но он будет использоваться совместно в рамках одного запроса. Другие распространённые варианты:
- transient - новый экземпляр создаётся при каждом использовании;
- singleton – создаётся один экземпляр на всё время жизни приложения.
Ключевые преимущества использования DI:
- Слабая связанность: Объекты не имеют жёстко заданных зависимостей. Это упрощает модификацию и расширение системы.
- Удобство тестирования: Зависимости можно заменять моками или заглушками во время тестирования, что упрощает написание тестов и повышает их надёжность.
- Централизованная конфигурация: Управление созданием объектов и разрешением зависимостей в одном месте делает код чище, а приложение — более масштабируемым.
Часто встречающийся неверный ответ
«В .NET достаточно добавить сервисы в файл Program.cs, используя builder.Services.AddSingleton() или любой аналогичный метод, и .NET автоматически обработает все требования DI без дополнительной настройки».
Этот ответ демонстрирует неполное понимание DI:
- Недостаток деталей о потреблении зависимостей: В ответе упоминается добавление сервисов в DI-контейнер, но не объясняется, как эти сервисы потребляются в приложении. Простая регистрация сервиса не завершает процесс DI; разработчики также должны внедрять эти сервисы в компоненты, где они необходимы.
- Чрезмерное упрощение DI: Ответ подразумевает, что DI не требует тщательного рассмотрения времени жизни сервисов или того, как зависимости внедряются в потребляющие классы. Он упускает из виду важность выбора правильного времени жизни сервиса (singleton, scoped, transient) в зависимости от варианта использования.
Такая ошибка обычно возникает, когда разработчик знает основы добавления сервисов в контейнер внедрения зависимостей, но ему не хватает более глубокого понимания того, как проектировать приложения, чтобы в полной мере использовать преимущества внедрения зависимостей.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
День 2524. #SystemDesign101
8 Распространённых Проблем Проектирования Систем и их Решения
1. Система с нагрузкой на чтение
- Используйте кэширование для ускорения операций чтения.
2. Большой трафик на запись
- Используйте асинхронные рабочие процессы для обработки операций записи.
- Используйте базы данных на основе LSM-деревьев.
3. Единая точка отказа
- Внедрите механизмы дублирования и отказоустойчивости для критически важных компонентов, таких как базы данных.
4. Поддержка доступности
- Используйте балансировку нагрузки, чтобы гарантировать, что запросы направляются к работоспособным экземплярам серверов.
- Используйте репликацию базы данных для повышения отказоустойчивости и доступности.
5. Высокие задержки
- Используйте сеть доставки контента (CDN) для уменьшения задержек.
6. Обработка больших файлов
- Используйте блочное и объектное хранилище для обработки больших файлов и сложных данных.
7. Мониторинг и уведомления
- Используйте централизованную систему логирования, например, стек ELK.
8. Медленные запросы к базе данных
- Используйте правильные индексы для оптимизации запросов.
- Используйте шардинг для горизонтального масштабирования базы данных.
Источник: https://bytebytego.com/guides/8-common-system-design-problems-and-solutions/
8 Распространённых Проблем Проектирования Систем и их Решения
1. Система с нагрузкой на чтение
- Используйте кэширование для ускорения операций чтения.
2. Большой трафик на запись
- Используйте асинхронные рабочие процессы для обработки операций записи.
- Используйте базы данных на основе LSM-деревьев.
3. Единая точка отказа
- Внедрите механизмы дублирования и отказоустойчивости для критически важных компонентов, таких как базы данных.
4. Поддержка доступности
- Используйте балансировку нагрузки, чтобы гарантировать, что запросы направляются к работоспособным экземплярам серверов.
- Используйте репликацию базы данных для повышения отказоустойчивости и доступности.
5. Высокие задержки
- Используйте сеть доставки контента (CDN) для уменьшения задержек.
6. Обработка больших файлов
- Используйте блочное и объектное хранилище для обработки больших файлов и сложных данных.
7. Мониторинг и уведомления
- Используйте централизованную систему логирования, например, стек ELK.
8. Медленные запросы к базе данных
- Используйте правильные индексы для оптимизации запросов.
- Используйте шардинг для горизонтального масштабирования базы данных.
Источник: https://bytebytego.com/guides/8-common-system-design-problems-and-solutions/
👍6👎1