.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 1840. #ЧтоНовенького #CSharp13
Улучшения SearchValues в .NET 9
Продолжаем заглядывать в будущее .NET.

Класс System.Buffers.SearchValues<T>, представленный в .NET 8, предназначен для эффективного поиска набора байтов или символов в другом наборе, например, в реализации String.IndexOfAny(char[]). При создании экземпляра SearchValues<T> все данные, необходимые для оптимизации будущего поиска, вычисляются заранее и выбирается наиболее эффективный алгоритм поиска.

Вот простой пример:
var searchValues = SearchValues.Create(
new[] { 'a', 'e', 'i', 'o', 'u' });

Console.WriteLine(
ContainsVowel("Hello, World!")); // True

bool ContainsVowel(ReadOnlySpan<char> text)
=> text.ContainsAny(searchValues);


Пока мы ограничены поиском только символов. Но гораздо чаще мы хотим искать строковые значения, то есть текст, внутри другого текста. В .NET 9 обещают это ввести! Появились новые перегрузки, и SearchValues можно будет применять в поиске текста:
var names = SearchValues.Create(
["Steven", "Sherlock", "Holmes", "Michael", "Scott"],
StringComparison.OrdinalIgnoreCase);

var text = "This is Steven and Michael. You know Michael Scott from the office.";

Console.WriteLine(
MemoryExtensions.ContainsAny(text, names)); // True


Кроме того, есть предложение добавить SearchValues в регулярные выражения.

Источник: https://steven-giesel.com/blogPost/080c8f82-f376-489f-a304-72d419978294/searchvalues-object-become-better-with-net-9
👍22
День 1841. #ВопросыНаСобеседовании #ASPNET
Самые часто задаваемые вопросы на собеседовании по C#

28. Как сервер Kestrel работает в среде .NET Core и чем он отличается от других веб-серверов?

Kestrel — кроссплатформенный веб-сервер, созданный для приложений на базе .NET Core. Его также можно использовать в сочетании с обратным прокси-сервером, таким как Apache, Nginx или IIS, который обеспечивает дополнительный уровень конфигурации, безопасности и балансировки нагрузки.

Ключевые особенности:
- Кроссплатформенность.
- Высокая производительность: оптимизирован для эффективной обработки большого количества одновременных подключений.
- Легковесность: оптимизирован для работы в средах с ограниченными ресурсами, таких как контейнеры.
- Повышенная безопасность: поддерживает HTTPS и защищён от уязвимостей веб-сервера.
- Широкая поддержка протоколов: HTTP/1.1, HTTP/2 и HTTP/3, WebSocket.
- Бесшовная интеграция с компонентами ASP.NET Core, такими как конвейер промежуточного ПО, внедрение зависимостей и система конфигурации.
- Множество рабочих нагрузок: ASP.NET (минимальные API, MVC, страницы Razor, SignalR, Blazor и gRPC), а также обратные прокси с помощью YARP.
- Расширяемость: возможность настройки с помощью конфигурации, промежуточного ПО и т.п.
- Встроенные функции диагностики производительности, такие как ведение журнала и метрики.

Место, которое Kestrel занимает в процессе обработки запроса показано на рисунке ниже. Обратный прокси (Apache, Nginx или IIS) пересылает запрос в веб-сервер ASP.NET Core (по умолчанию Kestrel). Kestrel принимает низкоуровневый сетевой запрос и использует его для создания объекта HttpContext, который может использовать остальная часть приложения. Веб-сервер заполняет HttpContext подробностями исходного HTTP-запроса и другими деталями конфигурации и передаёт их остальной части приложения.

Kestrel отвечает за получение данных запроса и создание представления запроса на C#, но не пытается обрабатывать запрос напрямую. Для этого он передаёт HttpContext в конвейер промежуточного ПО, где выполняются стандартные для всех запросов операции вроде журналирования, обработки исключений или обслуживания статических файлов.

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

Источники:
-
https://dev.to/bytehide/net-core-interview-question-answers-4bc1
- Эндрю Лок “
ASP.NET Core в действии”. 2-е изд. – М.: ДМК Пресс, 2021. Глава 2.
👍11👎1
День 1842. #Карьера
Сеньор, Эксперт или Кто? Начало
Кто такой сеньор? Как им стать? Имеет ли это вообще значение? Давайте рассмотрим некоторые аспекты при «оценке» чьих-либо навыков.

Зрелость
Что значит быть зрелым? Многие считают, что важно указывать на плохие стороны решений. Неважно, говорим ли мы о разработке ПО, дорогах, налогах или о чём-то ещё, что люди любят обсуждать. Хотя может показаться, что хорошие инженеры (в целом профессионалы) могут найти недостатки решений и определить, где они не работают, это не то, что нам нужно. Рассмотрим некоторые уровни профессионализма людей и их особенности.

1. Просто жаловаться
Это легко. «Налоги надо снизить». «Этот код не работает, надо исправить». Оба эти утверждения могут быть на 100% верными, но при этом совершенно бесполезными. Легко «просто жаловаться», сложно объяснить, почему что-то не так, и как это исправить. В разработке мы можем делать такие утверждения буквально обо всем: о языке программирования, технологии, библиотеке, фреймворке, конкретном решении или чём-то ещё. Это ничего не приносит.

2. Уметь жаловаться
Это сложнее. На этом уровне человек может легко указать на недостатки конкретного решения и объяснить, что они означают. Более того, человек часто оказывается прав. Например, может объяснить, почему эта библиотека недостаточно хороша, почему код нечитабелен, почему это решение работает медленно или может найти крайние случаи, когда решение потерпит неудачу. Но это всё равно не то, что нужно. Реальность никогда не бывает идеальной. Мы не можем сделать код (налоги, дороги и т.д.) идеальным и избежать всех проблем. Недостаточно просто показать, где что-то не работает и что есть недостатки, потому что недостатки есть у всего. Интересно, что многие архитекторы ПО находятся на этом уровне, потому что всегда могут сказать, «чего не делать», но никогда не смогут предложить лучшее решение.
Другой пример: программист может предложить множество вариантов, но не может решить, какой выбрать, потому что у всех есть какие-то проблемы. Позже они обычно навязывают решение клиенту, владельцу продукта или архитектору ПО, а затем могут жаловаться, что было принято неправильное решение.

3. Предоставлять решения
Эти люди могут высказывать идеи (и даже критиковать их) и принимать решения. Даже если они понимают, что всё не будет идеально, они могут пойти на риск или сознательно выбрать что-то, что не будет работать идеально.
Важно понимать, что, если человек сделает выбор, его всегда будут критиковать. Потому что невозможно выбрать идеальное решение. Поэтому, если вы хотите стать достаточно зрелыми и достичь этого уровня, вам нужно смириться с тем, что вас всегда будут критиковать. Неважно программист вы, архитектор ПО или политик.

Сеньорность
Что значит быть сеньором? Дело не только в навыках, а в правильном отношении, навыках и опыте.

1. Джун
Не может работать самостоятельно из-за недостатка опыта и навыков и нуждается в помощи и надзоре, чтобы добиться прогресса. Некоторые утверждают, что «X — не джунская позиция», где X может быть кем угодно (scrum-мастер, владелец продукта, менеджер проекта). Но, по сути, независимо от роли, на начальном этапе любому понадобится некоторая помощь и надзор.

2. Мидл
Может работать самостоятельно, но ничем не отличается от других. Он «просто делает свою работу». На него можно положиться, не нужно его контролировать, и можно ожидать, что у него достаточно навыков для выполнения работы.

3. Сеньор
Сеньоры понимают, в чём заключается их роль и как она взаимодействует с другими ролями вокруг. Они могут совершенствоваться в роли и не только в своей. Могут растить других, делиться знаниями, обучать и адаптироваться к меняющимся условиям.
Это не означает, что сеньоры обладают отличными навыками. Зачастую это не так. Но они могут эффективно использовать свои навыки и понимают, как повысить уровень себя и окружающих. Они используют весь свой потенциал и раскрывают весь потенциал окружающих.

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

Источник:
https://blog.adamfurmanek.pl/2023/02/24/types-and-programming-languages-part-19/index.html
👍12
День 1843. #Карьера
Сеньор, Эксперт или Кто? Окончание

Начало

Старший инженер, архитектор и другие
Означает ли изложенное вчера, что сеньорам не обязательно хорошо разбираться в программировании? Или что, чтобы стать архитектором, нужно учить других? Есть две основные вещи, которые мы можем выделить в разработке ПО: роли и навыки.

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

Навыки
Каждая роль требует навыков. Некоторые навыки могут потребоваться для многих должностей, например, работа с числами необходима и бухгалтерам, и продавцам. Можно измерить ваши навыки, и обычно можно сказать, что вы новичок, профессионал или эксперт. В разработке ПО мы можем распознать множество аспектов навыков:
1. Технология
Язык программирования, база данных, фреймворк, библиотека, парадигма, приложение или что-то ещё. Например, эксперт по C# сможет объяснить, как работает GC, как реализуются блокировки или почему мы не можем наследовать от структур.

2. Область
Определённая часть приложения: бэкенд, фронтенд, инфраструктура, база данных и т.п. Например, эксперт по веб-бэкенду сможет объяснить SNI, CORS, SOP, сертификаты, масштабирование, кэширование и другие вещи, обычно используемые в веб-серверах.

3. Домен
Определённая часть реальности, которую мы моделируем в ПО: платежи, медицина, торговля или логистика. Например, эксперт в банковском деле объяснит, что такое кредитный рейтинг, как его рассчитать, что такое PCI, как работать с международными транзакциями, что такое ELIXIR, SEPA или SORBNET.

Кто я?
Исходя из вышесказанного, возможно, вы старший инженер-программист + эксперт по бэкэнду + младший специалист в C# + профессионал в банковском деле. Именно поэтому и могут существовать архитекторы ПО, которые не умеют программировать.

Программист или разработчик ПО?
Программирование и разработка ПО - это лишь вопрос названия. Важно понимать, что «программирование» – не единственное, что есть в «разработке ПО». Легко написать код. Трудно поддерживать код на протяжении многих лет, когда технологии меняются, операционные системы устаревают, а фреймворки исчезают. Не имеет значения, называем ли мы себя программистами или разработчиками ПО. Важно то, сможем ли мы написать код или разрабатывать одну и ту же кодовую базу на протяжении десятилетий.

А теперь самое сложное. Когда кто-то ищет «сеньоров», можем ли мы сказать, кого они имеют в виду?
Короткий ответ: нет. Вы не узнаете, пока не спросите их, что они под этим подразумевают. И именно так следует искать работу: просто сопоставьте, каковы их ожидания, что вы умеете делать, насколько вы в этом хороши и что вы получаете взамен. Неважно, назовут ли вас джуном, сеньором, архитектором, директором, помощником или экспертом. Это вообще не имеет значения (если отбросить психологические аспекты). Важно то, какова ваша роль и чего от вас ожидают.

Источник: https://blog.adamfurmanek.pl/2023/02/24/types-and-programming-languages-part-19/index.html
👍8
Класс UserSignup имеет свойства Email и EmailConfirmation. Какой атрибут аннотаций данных использовать для свойства EmailConfirmation, чтобы проверить его равенство свойству Email?
#Quiz #ASPNET
Anonymous Quiz
28%
[Validate(x => x == this.Email)]
27%
[Equals("Email")]
18%
[Compare("Email")]
26%
[Match("Email")]
👍11
День 1844. #Шпаргалка
Примеры Кода для Повседневных Задач

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

II. Списки. Начало
Среди различных структур данных списки выделяются как наиболее распространённые. В C# список представляет собой «динамический массив». Мы можем добавлять и удалять элементы, не беспокоясь о лежащей в основе реализации, что делает списки интуитивно понятными в обращении.

1. Добавление элемента
C# предлагает множество способов добавления элементов в список:
// Статическая инициализация
var list = new List<int> {1, 2};
// Начиная с C# 12
List<int> list2 = [2, 3];

// Добавление - Add
list.Add(3); // [1, 2, 3]
// Добавление списка - AddRange
list.AddRange(new List<int> {4, 5});
// [1, 2, 3, 4, 5]

// Начиная с C# 12 – оператор расширения
List<int> list3 = [..list2, 4];
// [2, 3, 4]

// Вставка - Insert
list2.Insert(0, 1);
// [1, 2, 3]

// Вставка списка - InsertRange
list2.InsertRange(list2.Count, new List<int> {4, 5});
// [1, 2, 3, 4, 5]

См. подробнее про оператор расширения.

2. Получение последнего элемента
C# предлагает несколько интересных альтернатив:
List<string> list = ["red", "blue", "green"];

// Стандартно через Count
var last = list[list.Count - 1];
// LINQ
last = list.Last();
// индексатор
last = list[^1];


3. Проверка пуст ли список
И снова, есть несколько вариантов (на всякий случай, добавим проверку на null):
// Метод Count
if ((list?.Count ?? 0) == 0)
{
// список пуст
}
// LINQ
if (list?.Any() != true)
{
// список пуст
}


4. Клонирование списка
При работе со ссылочными типами всегда следует помнить, что простое присваивание их другой переменной копирует только ссылку:
List<int> list = [1, 2, 3];
// LINQ
var duplicate = list.ToList();
// Конструктор
duplicate = new List<int>(list);
// Начиная с C# 12 – оператор расширения
duplicate = [..list];


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

Источник:
https://medium.com/bytehide/100-csharp-code-snippets-for-everyday-problems-e913c786dec9
👍21
День 1845. #Шпаргалка
Примеры Кода для Повседневных Задач

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

II. Списки. Окончание
Начало

5. Генераторы списков
List comprehension — мощная функция Python, название которой сложно перевести на русский. Назовём это просто генерацией списков. В C# нет прямого эквивалента, но можно добиться аналогичной функциональности, используя LINQ:
List<int> list = [1, -2, 3];

// Преобразование
var squares = list
.Select(i => i*i).ToList();

// Фильтрация
var positive = list
.Where(i => i > 0).ToList();

// Все возможные пары
var list2 = new List<int> { 4, 5, 6 };
var pairs = list
.SelectMany(a => list2, (a, b) => (a, b))
.ToList();

И т.п.

6. Попарная обработка элементов двух списков
Представьте, что у вас есть два списка, и ваша цель — объединить их в один путём, например, попарного суммирования элементов:
List<int> list = [1, 2, 3, 4, 5, 6];
List<int> list2 = [4, 8, 15, 16, 23, 42];

// «В лоб» в цикле
var sum = new List<int>();
for (int i = 0; i < list.Count; i++)
sum.Add(list[i] + list2[i]);

// LINQ метод Zip
sum = list
.Zip(list2, (x, y) => x + y).ToList();

Замечание: в отличие от примера «в лоб», метод Zip корректно обрабатывает ситуации, когда списки разной длины. Длина результирующего списка будет равна длине меньшего из списков.

7. Сортировка
Сортировка строк (или сложных объектов) имеет свои особенности, в которые мы не будем здесь углубляться. Отметим, что у нас есть несколько вариантов:
List<string> list = ["leaf", "cherry", "Fish"];

// «На месте»
list.Sort();
// Без учёта регистра
list.Sort(StringComparer.OrdinalIgnoreCase);

// в новый список
var sorted = list
.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
.ToList();

Если требуется сортировка объектов по какому-либо свойству, метод Sort также может принимать компаратор или лямбда-выражение для сравнения объектов между собой. См. подробнее в конце этого поста.

Источник: https://medium.com/bytehide/100-csharp-code-snippets-for-everyday-problems-e913c786dec9
👍12
День 1846. #ЗаметкиНаПолях
Используем Redis в .NET 8

Redis — это хранилище данных в памяти с открытым исходным кодом, используемое в качестве БД, кэша или брокера сообщений. Он отличается скоростью и гибкостью, что делает его отличным выбором для кэширования в современных приложениях. Сегодня рассмотрим, как использовать Redis для кэширования в приложении .NET 8.

Настройка Redis с Docker в Windows
Docker предоставляет простой и понятный способ запуска Redis без необходимости установки Redis на машину. Установите Docker Desktop для Windows, если необходимо. Убедитесь, что функция WSL 2 включена.

Запустите Docker Desktop. В первый раз это может занять несколько минут. Откройте окно терминала и запустите Redis в контейнере Docker:
docker run --name my-redis -p 6379:6379 -d redis

Здесь:
--name my-redis – имя контейнера.
-p 6379:6379 – привязка порта контейнера к порту машины.
-d – запускает контейнер в независимом режиме (если вы закроете окно терминала, контейнер продолжит работу).
redis – использует образ Redis из Docker Hub.

Проверим, что контейнер запущен:
docker ps

Вы должны видеть my-redis среди запущенных контейнеров. Чтобы остановить контейнер, выполните команду:
docker stop {container_id}

используя container_id, показанный в выводе команды docker ps.

Использование Redis в приложении
Создадим новое консольное приложение:
dotnet new console -n HelloRedis
cd HelloRedis

Добавим пакет StackExchange.Redis:
dotnet add package StackExchange.Redis


Вот пример программы с сохранением объекта в кэш и получением из него, используя сериализацию/десериализацию:
using System.Text.Json;
using StackExchange.Redis;

// Подключаемся к серверу Redis
var redis = ConnectionMultiplexer
.Connect("localhost");
var db = redis.GetDatabase();

var user = new User(1, "John Doe", "[email protected]");

// Сохраняем объект с ключом "user:1".
await db.StringSetAsync("user:1",
JsonSerializer.Serialize(user));

// Получаем объект из Redis
var cached = await db.StringGetAsync("user:1");
if (cached is null)
{
Console.WriteLine("Не найдено в кэше");
return;
}

var cachedUser =
JsonSerializer.Deserialize<User>(cached);
if (cachedUser is null)
{
Console.WriteLine("Ошибка десериализации");
return;
}

Console.WriteLine($"User: {cachedUser.Name}, Email: {cachedUser.Email}");

public record User(int Id, string Name, string Email);

Полный пример можно посмотреть на GitHub.

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

Источник: https://ardalis.com/hello-redis-getting-started-with-redis-dotnet/
👍23👎1
День 1847. #Testing
Компромиссы при Написании Тестов

Многие компании стремятся иметь 100% покрытия кода. Даже делают частью процесса CI/CD проверки, чтобы гарантировать, что покрытие тестами всегда увеличивается. Это имеет несколько последствий.

Запланированный эффект - разработчики пишут больше тестов. Незапланированный - иногда они пишут плохие тесты, либо просто костыли, чтобы «обмануть» проверку. Например, если вы провели рефакторинг хорошо протестированного кода, уменьшив его объём, покрытие кода уменьшится, но кодовая база улучшится.

Стремиться к 100% покрытию кода — плохая идея, но где провести черту?

Зачем мы пишем тесты?
В конечном счёте тесты обслуживают код, который мы пишем, и предназначены для решения проблемы. Если добавление теста не помогло решить проблему, это лишь трата времени и денег. Тесты снижают риск, позволяют проверить вашу работу и убедиться, что она скорее всего верна. Каждый тест даёт вам немного больше уверенности в тестируемом коде. Ценность тестового кода не прямая. Она в предотвращении потерь, как с точки зрения реального ущерба от ошибок (потеря дохода, нарушение конфиденциальности, неверные результаты), так и с точки зрения времени на обнаружение и исправление этих ошибок. Нам не нравится платить эту цену, поэтому вместо этого мы платим за тесты. Это страховка.

Какие риски вы хотите покрыть?
Как страховые полисы имеют разное покрытие, лимиты и доп. услуги за дополнительную цену, так с количеством тестов. Мы не можем позволить себе покрыть все риски. Нужно выбрать, сколько платить в виде «страховой премии» и сколько - в случае аварии. При 100% покрытии кода вы хотите избежать риска любой ошибки. А если тестов нет, значит даже серьёзные ошибки с максимальной стоимостью вас не беспокоят.

Как выбрать, какой риск мы хотим покрыть при тестировании? Часто это неявное решение: кто-то считает, что «больше покрытия кода, это хорошо», а затем люди начинают писать больше тестов, потому что «это наша культура, чувак»! Лучший способ – обдумать решение.

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

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

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

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

Как вы решаете это в своей команде? У вас есть цель обеспечить покрытие кода?

Источник: https://ntietz.com/blog/too-much-of-a-good-thing-the-cost-of-excess-testing/
👍2
День 1848. #ЧтоНовенького
Новые Метрики
ASP.NET Core и Панели Grafana в .NET 8.
Метрики - это числовые измерения, которые с течением времени сообщают диагностические данные о вашем приложении. В .NET 8 добавлены несколько полезных метрик:
- Количество и продолжительность HTTP-запросов
- Количество активных HTTP-запросов
- Результаты сопоставления маршрутов
- Данные об ограничении трафика и продолжительности очереди
- Использование SignalR
- Низкоуровневое соединение и использование TLS от Kestrel
- Диагностика обработки ошибок
и другие.

Например, продолжительность обработки каждого HTTP-запроса записывается в метрике http.server.request.duration. Такие инструменты, как .NET OpenTelemetry SDK, затем могут быть настроены для экспорта данных метрик в хранилище телеметрии, например Prometheus или Azure Monitor. Метрики являются частью стандарта OpenTelemetry, и все современные инструменты поддерживают их.

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

Использование метрик
Встроенные метрики ASP.NET Core записываются автоматически. Как вы будете использовать эти показатели, зависит от вас. Рассмотрим некоторые из доступных вариантов.

Панель мониторинга .NET Aspire
Имеет простой и удобный UI для просмотра структурированных журналов, трассировок и метрик. Приложения Aspire автоматически настраиваются для отправки данных телеметрии на панель управления во время разработки. Пользовательский интерфейс Aspire включает фильтры метрик. Фильтрация возможна с использованием мощной функции метрик: атрибутов.

Каждое записываемое значение метрики помечается метаданными, называемыми атрибутами. Например, http.server.request.duration записывает продолжительность HTTP-запроса вместе с атрибутами этого запроса: адрес сервера, метод HTTP, сопоставленный маршрут, код состояния ответа и т. д. Затем можно отфильтровать атрибуты, чтобы получить нужные данные:
- Продолжительность обработки запроса к определённой конечной точке.
- Количество запросов с кодами ответа HTTP 4xx.
- Количество посетителей, использующих HTTP/1.1 и HTTP/2.
и т.п.

Панели мониторинга ASP.NET Core Grafana
Grafana — мощный инструмент с открытым исходным кодом для создания расширенных информационных панелей и оповещений. Grafana — хороший выбор для мониторинга приложений, развёрнутых в рабочей среде, а панель мониторинга даёт представление о работоспособности и использовании приложений в режиме реального времени. Однако для настройки качественных информационных панелей требуется время. В рамках добавления метрик в .NET 8 команда .NET создала готовые информационные панели, предназначенные для встроенных метрик ASP.NET Core.

Панели мониторинга ASP.NET Core Grafana доступны для загрузки на сайте grafana.com. Вы можете использовать панель мониторинга как есть или дополнительно настроить её для создания решения, адаптированного к вашим потребностям.

А вот пример приложения, использующего метрики.

Кроме того
- Метрики не ограничиваются тем, что встроено в .NET. Вы можете создавать собственные метрики для своих приложений.
- dotnet-counters — инструмент командной строки, который может просматривать текущие метрики для приложений .NET Core по запросу. Он не требует настройки, что делает его полезным для поиска проблем или проверки работы метрик.
- Вы можете использовать метрики в модульных тестах.

Источник: https://devblogs.microsoft.com/dotnet/introducing-aspnetcore-metrics-and-grafana-dashboards-in-dotnet-8/
👍10
День 1849. #ProjectManagement
Отслеживаем Архитектурные Решения через ADR
При проектировании архитектуры системы у вас есть много вариантов выбора. Вы архитектор, делаете некоторый выбор, всё идёт хорошо несколько месяцев. И вдруг появляется новое требование. Оно заставляет задуматься: «Правильна ли эта архитектура? Стоит ли что-то менять?». Вы уже не помните причины своего выбора. Нужно что-то, что напоминало бы вам, почему вы сделали тот или иной выбор…

Реестр Архитектурных Решений (Architecture Decision Records, ADR) — это способ описания, отслеживания и обсуждения архитектурных и проектных решений. Он позволяет иметь чётко определённый процесс принятия обоснованных решений и отслеживать причины вашего выбора.

Структура
ADR — это не просто список решений. Это документ, в котором также отслеживается текущий контекст, рассматриваемые альтернативы и последствия окончательного выбора. ADR состоит из набора файлов, каждый из которых описывает решение.
1. Суть решения
Например, «Использовать Azure Service Bus для очередей».
2. Статус
Например, «На обсуждении», «Принято» и «Заменено».
3. Время принятия решения
Когда ADR создан и когда получал каждый статус.
4. Контекст
Например: «В настоящее время мы используем Azure в качестве основного поставщика».
5. Последствия
Например: «Мы согласны с привязкой к поставщику. Так будет легче получить поддержку со стороны команды по инфраструктуре».

Статусы
Обычно должен быть строгий набор статусов, гарантирующий, что после достижения одного из окончательных статусов решение в ADR не может быть изменено.
Первичные
- Черновик: первичное описание решения, вы пока не готовы его обсуждать.
- Предложение: объяснены все детали решения для официального обсуждения.
- Открыто: каждый может добавлять комментарии к решению, что позволит учесть другие точки зрения и указать слабые места.
Окончательные
- Принято: команда согласна с решением, и теперь можно его реализовать.
- Отклонено: решение неосуществимо, реализация невозможна.
- Устарело: решение больше не является полезным или необходимым.
- Заменено: другой ADR заменяет нынешний. Обязательно нужно указать номер нового ADR, а в новом сослаться на тот, который он заменяет. Так можно восстановить историю конкретного выбора.

Рекомендации
1. Используйте единый формат и структуру для каждого ADR.
2. Храните ADR в текстовом файле рядом с кодом, соответствующим этому решению, или в центральном репозитории, чтобы можно было просмотреть историю обновлений.
3. Используйте понятное и описательное имя файла, например ADR-001-use-azure-service-bus.md.
4. Делайте ADR кратким и сосредоточенными на одном решении. Добавляйте только необходимую информацию, чтобы понять причину решения.
5. Обновляйте ADR по мере развития или изменения решения, указывая причину изменений.
6. Периодически проверяйте актуальность ADR текущему состоянию архитектуры и потребностям бизнеса.

Вот хороший список шаблонов ADR.

Инструменты
- adr-cli - инструмент CLI, написанный на .NET, который автоматически создаёт и управляет историей ваших ADR.
- ADR Manager - UI инструмент, который подключается к вашему репозиторию GitHub и генерирует файлы ADR.

Итого
ADR — отличный инструмент, особенно для проектов, требования которых не определены заранее и которые, как ожидается, будут иметь длительный срок службы. Одним из наиболее важных этапов принятия архитектурных решений является обсуждение решения. Архитектор не должен навязывать архитектуру команде. Другие заинтересованные стороны, например разработчики, могут иметь некоторые опасения или заметить случай, который не может быть охвачен решением.

Источник: https://www.code4it.dev/architecture-notes/architecture-decision-records/
👍13👎1
День 1850.
Сделал лазерную коррекцию зрения.

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

Существуют ещё 2 метода, более (сильно более) дешёвых. LASIK и ФРК. LASIK я хотел делать изначально, но по результатам обследования у меня выявили тонкую роговицу (наследственное), а при этом такой вид коррекции делать не рекомендуется. Предложили ФРК, но там довольно длительный и муторный процесс восстановления, поэтому поехал в Питер на SMILE.

Пока идёт третий день после операции, так полностью эффекта ещё не ощутил. Да и у меня было всего -1,75. Не то, чтоб совсем не видел. На следующий день после операции зрение проверили, сказали, что сейчас 0,9-1. Мне объясняли, но я так до конца и не понял, чем отличаются диоптрии (+/- 1,2,3,4…) от остроты зрения (0,1-1). Но суть в том, что у меня как раз был небольшой минус, а вот острота зрения была всего 0,2 (20%).

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

Операция абсолютно безболезненная (только страшно, потому что что-то делают с глазами) и иногда неприятно, например, когда ставят расширитель на веки или когда вентикулу вынимают. И главное не дёргаться, что у меня получалось с трудом. Чуть не завалил весь процесс. Заняло всё 15 минут от входа в операционный отсек до выхода: переодевания, подготовка, операция. После идёшь домой и желательно поспать. В первый день ощущения не из приятных. Как будто постоянно смотришь на солнце (даже с закрытыми глазами). Но в принципе терпимо, через 4-6 часов это проходит. Остаётся небольшое помутнение зрения из-за воспаления, оно должно постепенно проходить и пройти полностью через пару недель.

Вот такие приключения.

Если кто тоже думает делать и имеет вопросы, спрашивайте. А кто делал, делитесь опытом в комментариях.
👍56
День 1851. #Шпаргалка
Примеры Кода для Повседневных Задач
В этой серии представлю вам коллекцию фрагментов кода C#, охватывающих широкий спектр сценариев, с которыми вы можете столкнуться при разработке ПО.


III. Операции с файлами
Операции с файлами являются общим требованием во многих задачах. В этом разделе мы рассмотрим, как работать с файлами и каталогами в C#, используя пространство имён System.IO.

1. Чтение текстового файла
Чтобы прочитать текстовый файл в C#, вы можете использовать класс File и его метод ReadAllText():
var path = "example.txt";
var content = File.ReadAllText(path);
Console.WriteLine(content);


2. Запись текстового файла
Чтобы записать текстовый файл, можно использовать метод WriteAllText() класса File:
var path = "output.txt";
var content = "Hello, World!";
File.WriteAllText(path, content);


3. Дозапись текста в файл
Чтобы добавить текст в текущий файл, можно использовать метод AppendAllText():
var path = "log.txt";
var logEntry = "New log entry";
File.AppendAllText(path, logEntry);


4. Чтение файла построчно
Для чтения файла построчно подойдёт метод ReadLines():
var path = "example.txt";
foreach (var line in File.ReadLines(path))
Console.WriteLine(line);


5. Создание/удаление папки
Для создания/удаления папки используйте соответственно методы CreateDirectory() и Delete() класса Directory:
var path = "new_directory";
Directory.CreateDirectory(path);
Directory.Delete(path, true);

Второй параметр метода Delete указывает, надо ли удалять всё содержимое папки.

6. Проверка существования
Чтобы проверить существование файла или папки, используйте метод Exists() класса File или Directory соответственно:
var fPath = "example.txt";
var dPath = "directory";

var fileExists = File.Exists(fPath);
var dirExists = Directory.Exists(dPath);


7. Список файлов в папке
Используйте метод GetFiles() класса Directory:
var path = " directory";
var files = Directory.GetFiles(path);

foreach (var f in files)
Console.WriteLine(f);


8. Копия/перемещение файла
Используйте соответственно методы Copy() или Move() класса File:
var source = "example.txt";
var copy = "copy_example.txt";
var moved = "moved_example.txt";

File.Copy(source, copy);

File.Move(source, moved);


Источник: https://medium.com/bytehide/100-csharp-code-snippets-for-everyday-problems-e913c786dec9
👍17👎1
День 1852. #юмор
А вам тяжело расставаться с проектами?
👍21👎1
День 1853. #ЗаметкиНаПолях
Автоматически Регистрируем Конечные Точки в Минимальных API

В минимальных API регистрация каждой конечной точки через app.MapGet, app.MapPost и т.п. может привести к появлению повторяющегося кода. По мере роста проекта этот ручной процесс становится всё более трудоёмким и загромождать файл Program.cs. Можно попробовать группировать конечные точки, используя методы расширения, но это похоже на переизобретение контроллеров.

Автоматическая регистрация минимальных API значительно упрощает разработку, делая кодовую базу более лаконичной и улучшает удобство сопровождения за счёт создания централизованного механизма регистрации.

Сначала создадим интерфейс. Метод MapEndpoint принимает IEndpointRouteBuilder, который мы сможем использовать для вызова MapGet, MapPost и т.п.
public interface IEndpoint
{
void MapEndpoint(IEndpointRouteBuilder app);
}

Каждая реализация IEndpoint должна содержать ровно одно определение конечной точки. Ничто не мешает регистрировать несколько, но лучше не стоит.
public class GetUserStats : IEndpoint
{
public void MapEndpoint(IEndpointRouteBuilder app)
{
app.MapGet(
"users/{id}/stats",
async (Guid id) =>
{
return await GetStats(id);
}
);
}
}


Теперь используем рефлексию для сканирования сборок .NET и поиска классов, реализующих IEndpoint:
public static IServiceCollection AddEndpoints(
this IServiceCollection services,
Assembly assembly)
{
var svcs = assembly.DefinedTypes
.Where(t => t is
{ IsAbstract: false, IsInterface: false } &&
t.IsAssignableTo(typeof(IEndpoint)))
.Select(t =>
ServiceDescriptor.Transient(typeof(IEndpoint), t))
.ToArray();

services.TryAddEnumerable(svcs);
return services;
}


Наконец создадим метод расширения для регистрации конечных точек. Мы ищем все регистрации IEndpoint. Это будут классы конечных точек, которые мы теперь можем зарегистрировать в приложении, вызвав их метод MapEndpoint. Здесь мы также добавим параметр RouteGroupBuilder для возможности группировки конечных точек.
public static IApplicationBuilder MapEndpoints(
this WebApplication app,
RouteGroupBuilder? rgb = null)
{
var endpoints = app.Services
.GetRequiredService<IEnumerable<IEndpoint>>();

// Регистрируем в группу, если она задана.
// Если нет, то на уровне приложения
var builder = rgb is null ? app : rgb;

foreach (IEndpoint ep in endpoints)
ep.MapEndpoint(builder);

return app;
}


Вот как будет выглядеть файл Program.cs:
- AddEndpoints регистрирует реализации IEndpoint,
- MapEndpoints регистрирует конечные точки.
var builder = 
WebApplication.CreateBuilder(args);

builder.Services.AddEndpoints(
typeof(Program).Assembly);

var app = builder.Build();

app.MapEndpoints();
app.Run();


Итого
Автоматическая регистрация конечных точек в минимальных API может значительно повысить эффективность разработки и удобство сопровождения проекта. Хотя важно признать потенциальное влияние рефлексии на производительность при запуске приложения, а также на возможность использовать Native AOT. Потенциальным улучшением может стать использование генераторов кода для логики регистрации.

Полный код проекта тут.

Источник: https://www.milanjovanovic.tech/blog/automatically-register-minimal-apis-in-aspnetcore
👍14
День 1854. #ЧтоНовенького
Планы на .NET 9

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

Платформа для облачной разработки
В .NET 9 будет продолжено развитие облачных технологий с упором
на производительность во время выполнения и мониторинг приложений. Также будет уделено внимание упрощению интеграции с популярными сервисами, например, Kubernetes или Redis. Все эти возможности объединены в .NET Aspire, что значительно снизит стоимость и сложность создания облачных приложений.

Продолжится разработка Native AOT и тримминг приложений как ключевых инструментов для оптимизации рабочих приложений. В .NET 8 оптимизированы приложения веб-API, в .NET 9 будут оптимизированы другие типы приложений, а также обещано дальнейшее улучшение сборщика данных для динамической адаптации к размерам приложений (DATAS).

В Azure Container Apps позаботятся о том, чтобы приложения .NET 9 можно было легко масштабировать до нескольких экземпляров в их Kubernetes-среде. Также планируются улучшения безопасности при работе с такими данными, как токены защиты от подделки и токены аутентификации, и оптимизация API ограничений при работе с несколькими экземплярами.

Пример приложения eShop, который был продемонстрирован на .NET Conf в прошлом году, будет обновлён, чтобы использовать преимущества этих новых возможностей и вариантов развертывания по мере развития .NET 9 в течение года.

Visual Studio и VS Code будут включать новые возможности разработки и развёртывания для .NET Aspire: настройку компонентов, отладку (включая горячую перезагрузку) хоста приложений и дочерних процессов, а также полную интеграцию с панелью разработчика. Разработчики смогут развёртывать свои проекты в приложениях-контейнерах Azure из Visual Studio, VS Code и с помощью Azure Developer CLI.

Дорожная карта .NET 9
Здесь можно посмотреть детальные дорожные карты по разработке новинок в каждой сфере, будь то C#, ASP.NET, Entity Framework или Roslyn. Пока многие из них пустые, но в скором времени будут обновлены.

PS: если вы ждали Discriminated Unions в C#, то их там (пока) нет. Вообще новинок в C# довольно мало, и они сильно узкоспециализированные. Надеюсь, что в течение года появится что-то более интересное.

Источник: https://devblogs.microsoft.com/dotnet/our-vision-for-dotnet-9/
👍13
День 1856. #ЗаметкиНаПолях
Получаем Все Исключения из Parallel.ForEachAsync

На C# вы можете написать асинхронный код, похожий на синхронный код. Люди, использующие async/await, ожидают, что смогут перехватывать исключения, как в синхронном коде. Это очень удобно, но иногда вам нужно получить все исключения из асинхронного метода.
try
{
// Это выбросит несколько исключений
await Parallel.ForEachAsync(
Enumerable.Range(1, 10),
async (i, _) =>
{
throw new Exception(i.ToString());
});
}
// Обработает только первое исключение
// Остальные потеряются
catch (Exception ex)
{
// Выведет случайное число от 1 до 10,
// в зависимости от порядка обработки
Console.WriteLine(ex.ToString());
}


Если вы хотите получить все исключения, необходимо использовать Task.Exceptions или метод, предоставляющий содержимое этого метода, например Task.Wait.
try
{
await Parallel.ForEachAsync(
Enumerable.Range(1, 10),
async (i, _) =>
{
throw new Exception(i.ToString());
}).WithAggregateException();
}
catch (Exception ex)
{
// Выведет все исключения,
// т.к. ex - это AggregateException
Console.WriteLine(ex.ToString());
}

internal static class TaskExtensions
{
public static async Task
WithAggregateException(this Task task)
{
// Отключаем выброс исключений,
// Они будут обработаны в task.Wait()
await task.ConfigureAwait(
ConfigureAwaitOptions.SuppressThrowing);

// Задача уже выполнена, поэтому Wait только
// выбросит AggregateException при ошибке
task.Wait();
}
}


Заметьте, что Parallel.ForEachAsync прекращает обработку элементов из коллекции после выброса первого исключения. Он ожидает завершения текущих задач, а затем сообщает об исключениях. Количество выполненных задач будет зависеть от количества параллельно выполняемых операций. Например, если задать для Parallel.ForEachAsync опцию
var options = new ParallelOptions() { 
MaxDegreeOfParallelism = 3
};

то будут выполнены только 3 задачи.
Чтобы этого избежать, можно внутри задачи сохранять выброшенные исключения:
var exceptions = new ConcurrentBag<Exception>();
await Parallel.ForEachAsync(
Enumerable.Range(1, 10),
async (i, _) =>
{
try
{
throw new Exception(i.ToString());
}
catch (Exception ex)
{
exceptions.Add(ex);
}
});

if (!exceptions.IsEmpty)
throw new AggregateException(exceptions);


Источник: https://www.meziantou.net/getting-all-exceptions-thrown-from-parallel-foreachasync.htm
👍25