.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
День восемьсот пятьдесят седьмой. #ЗаметкиНаПолях
Настройка Производительности в EF Core
Сегодня посоветую вам видео из серии EF Core Community Standup, посвящённое настройке производительности в Entity Framework. Для знающих SQL, вроде меня, советы могут показаться довольно очевидными, хотя интересно было узнать, что оптимизации можно делать не в SQL, а прямо в LINQ запросе к EF.

Для примера взято приложение книжного магазина. Есть основная сущность Books. Сущности Authors (авторы) и Tags (теги), которые относятся к Books как многие-ко-многим, а сущность Reviews (отзывы и оценки) – один-ко-многим.
Нам для отображения доступных книг нужно выбрать данные книги (название, цена, год публикации), авторов и теги для каждой книги, а также количество отзывов и среднюю оценку.

Очевидным вариантом запроса будет что-то вроде:
var books = context.Books
.Include(book => book.AuthorsLink
.OrderBy(bookAuthor => bookAuthor.Order))
.ThenInclude(bookAuthor => bookAuthor.Author)
.Include(book => book.Reviews)
.Include(book => book.Tags)
.ToList();

Как можно улучшить этот запрос:
1. Добавить метод AsNoTracking() для всех запросов, не предполагающих изменения данных.

2. Не загружать ненужные данные. Используйте проекцию в методе Select для выбора только нужных полей:
Books.Select(p => new BookListDto {
Title = p.Title,
Price = p.Price,
Published = p.Published,


3. Не включать зависимости, а выбирать только то, что надо из них. Например, нам нужны только имена авторов через запятую:
  AuthorsOrdered = string.Join(", ",
p.AuthorsLink.OrderBy(q => q.Order)
.Select(q => q.Author.Name)),

String.Join будет выполняться на клиенте, но можно использовать пользовательские функции SQL (User-Defined Functions).

4. По возможности выполнять вычисления на стороне БД. Например, для количества отзывов и средней оценки (заметьте, что требуется приведение к обнуляемому типу и тип зависит от конкретной СУБД):
  ReviewsCount = p.Reviews.Count(),
ReviewsAverageVotes =
p.Reviews.Select(y =>
(double?)y.NumStars).Average(),


5. В конфигурации сущностей EF добавить индексы для каждого свойства сущности, по которому планируется производить сортировку или фильтрацию.

Чтобы не загромождать запрос, все эти изменения можно разместить в методе расширения MapBookToDto(this IQueryable<Book> books), таким образом сам запрос будет выглядеть примерно так:
var booksQuery = _context.Books 
.AsNoTracking()
.MapBookToDto()
. …

Если эти советы всё равно не дают достаточной производительности, можно, например, денормализовать БД и включить столбцы с рассчитанными значениями количества отзывов и средней оценкой в таблицу Books. А при добавлении/удалении отзыва пересчитывать эти значения.

В видео по ссылке ниже весь процесс оптимизации производительности описан более подробно и наглядно.

Источники:
- Видео
https://youtu.be/VgNFFEqwZPU
- Текст
https://www.thereformedprogrammer.net/five-levels-of-performance-tuning-for-an-ef-core-query/
День восемьсот пятьдесят восьмой. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
87. Две Ошибки Могут «Исправить» Друг Друга (и Это Трудно Обнаружить)
Код никогда не лжёт, но может сам себе противоречить. Иногда такие противоречия могут заставить схватиться за голову и воскликнуть: «Как это вообще может работать?»

В своём интервью главный разработчик ПО лунного модуля Аполлона-11 Аллан Клумпп рассказал, что ПО управления двигателями, содержало ошибку, которая должна была сделать посадочный модуль нестабильным. Однако эта ошибка компенсировалась другой, и ПО использовалось как для посадки на Луну, так и для Аполлона-11, прежде чем была обнаружена или исправлена.

Рассмотрим функцию, возвращающую статус завершения задачи. Представьте, что она возвращает false, когда должна возвращать true. Теперь представьте, что вызывающая функция не проверяет возвращаемое значение. Всё работает нормально, пока однажды кто-нибудь не заметит упущенную проверку и не добавит её.

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

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

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

Это происходит не только в коде: проблема также существует в документации и требованиях. И она может вирусно распространяться из одного места в другое. Ошибка в коде может компенсировать ошибку в документации.

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

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

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

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Allan Kelly
День восемьсот пятьдесят девятый. #ЧтоНовенького
.NET 6: Улучшения коллекций. Начало

1. Ёмкость списка, стека и очереди
Перед добавлением большого количества элементов в Dictionary или HashSet полезно вызвать EnsureCapacity с ожидаемым размером коллекции. Это позволяет коллекции выполнить одну операцию изменения размера заранее, избегая необходимости делать это несколько раз. Метод EnsureCapacity был добавлен в классы List<T>, Stack<T> и Queue<T>, также для повышения производительности.

Заметным исключением из этой группы является Collection<T>. В отличие от других коллекций, Collection<T> может при желании обернуть другую коллекцию, которая не обязательно предоставляет метод EnsureCapacity. Её потомок, ObservableCollection<T>, также не может предоставить метод EnsureCapacity.

Это не единственный недостаток Collection<T> и ObservableCollection<T>. Отсутствие метода AddRange давно раздражало разработчиков. А также им не хватает высокопроизводительного IEnumerator на основе структур, который предлагает List<T>.

2. Изменяемые структуры и словари
Хотя и редко, разработчикам иногда приходится работать с изменяемыми структурами. Это может вызывать трудности, потому что легко случайно скопировать изменяемую структуру, в результате чего два значения больше не будут синхронизироваться.

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

Чтобы избежать этих ненужных копий, изменяемые структуры обычно хранились в массивах. В отличие от свойства-индексатора в List<T>, доступ к элементам массива осуществляется напрямую.

В C# 7 были введены ссылочные возвращаемые значения (ref return). Это позволило индексаторам возвращать ссылку на объект, а не копию.
public ref int this[int index] {
get { return ref _internalArray[index]; }
}

Этот метод используется структурой Span<T>, начиная с .NET Core 2.1. Начиная с .NET 5, метод CollectionsMarshal.AsSpan позволяет легко получить span-обёртку коллекции.

Чтобы предложить те же возможности для словарей, была создана новая функция под названием CollectionsMarshal.GetValueRefOrNullRef. Есть несколько интересных фактов об этой функции:

1. Это не метод расширения. Разработчики опасались, что его будет слишком легко использовать неправильно, поэтому они намеренно сделали так, чтобы его было сложно найти. (Функцию AsSpan также потенциально небезопасно использовать, поскольку размеры коллекции не могут быть изменены, пока существует Span<T>.)

2. Существует внутренний метод Dictionary<TKey, TValue>.FindValue. Он и используется в GetValueRefOrNullRef:
public static ref TValue 
GetValueRefOrNullRef<TKey, TValue>
(Dictionary<TKey, TValue> dictionary, TKey key)
where TKey : notnull
=> ref dictionary.FindValue(key);

FindValue сам по себе использует некоторые методы, которые не распространены в программировании на C#, вроде goto, включая обратные переходы.

3. После вызова GetValueRefOrNullRef нужно использовать CompilerServices.Unsafe.IsNullRef, чтобы проверить, является ли результат ссылкой на фактическое значение или на null. Это связано с тем, что в C# нет синтаксиса для проверки того, является ли ссылка на структуру нулевой.

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

Источник:
https://www.infoq.com/news/2021/06/Net6-Collections/
День восемьсот шестидесятый. #ЧтоНовенького
.NET 6: Улучшения коллекций. Окончание

3. Улучшения сериализации для словарей
В то время как сериализация списочных коллекций довольно проста, коллекции, для которых требуется компонент хеширования, такие как Dictionary и HashSet, создают дополнительные проблемы. Одна из таких проблем - необходимость сериализации не только содержимого, но и используемого алгоритма сравнения. Чтобы десериализатор знал, должен ли он создавать коллекцию с порядковым компаратором, зависящим от языка и региональных параметров и чувствительным или нечувствительным к регистру.

Поскольку подавляющее большинство этих проблем связано со строковыми ключами, было решено добавить два новых метода в StringComparer:
public static bool IsWellKnownOrdinalComparer(
IEqualityComparer<string?>? comparer,
out bool ignoreCase);
public static bool IsWellKnownCultureAwareComparer(
IEqualityComparer<string?>? comparer,
[NotNullWhen(true)] out CompareInfo? compareInfo,
out CompareOptions compareOptions);

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

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

4. Очередь с приоритетом
Новый класс PriorityQueue разработан для сценариев, когда разработчику нужна очередь, в которой элементы помещаются в список на основе значения приоритета. Это можно реализовать несколькими способами, многие из которых зависят от вопроса: «Можно ли изменять приоритет элемента?».

Для PriorityQueue было решено не допускать изменения приоритета. Создатели определили, что фиксированное значение приоритета приведёт к 2-3-кратному улучшению производительности.

Чтобы гарантировать, что приоритет не может быть изменён, он хранится в очереди отдельно от объекта:
public void Enqueue(TElement element, TPriority priority);

Следующий вопрос - стабильность. Если вы добавляете в очередь два элемента с одинаковым приоритетом, и они гарантированно извлекаются из очереди в том же порядке, то очередь считается стабильной. В .NET решили не предоставлять стабильную очередь, опять же во имя быстродействия.

Ещё вопрос: разрешить ли перечисление очереди. На первый взгляд он может показаться странным. Почему бы не использовать foreach для очереди?

Первая проблема - это подразумеваемый контракт для IEnumerable. Большинство разработчиков предполагают, что вы можете дважды перечислить одну и ту же коллекцию и оба раза получить одинаковый результат. Это предположение встроено в общие шаблоны, где не всегда очевидно, что перечисление вообще происходит. Например, вызов метода Count() может перечислить элементы, что приведёт к опустошению очереди. Что означает, что вызванный после этого foreach увидит пустую очередь.

Можно сделать перечислитель неразрушающим, но в этом есть свои проблемы. Внутренне приоритетная очередь не обязательно упорядочена. Это означает, что перечислитель должен будет отслеживать, какие элементы он уже видел. Если по какой-то причине вам понадобится запустить несколько перечислений одновременно, каждый, по идее должен был бы получить полную копию очереди.

Таким образом, было решено, что класс PriorityQueue не будет реализовывать IEnumerable<T>. Это, в свою очередь, означает, что он также не может реализовать ICollection<T> или другие подобные интерфейсы.

Если вы действительно хотите использовать PriorityQueue в foreach, можно добавить метод расширения:
public static IEnumerable<TElement> 
GetDequeuingEnumerator<TElement, TPriority>(
this PriorityQueue<TElement, TPriority> queue)
{
while (queue.TryDequeue(out var item))
yield return item;
}

Источник: https://www.infoq.com/news/2021/06/Net6-Collections/
День восемьсот шестьдесят первый.
5 Инструментов для Обнаружения и Устранения Уязвимостей в Безопасности

Сканирование зависимостей
Своевременное обновление зависимостей - один из самых лёгких способов предотвращения уязвимостей в вашем коде.

1. Dependabot
Эта утилита GitHub имеет простой и понятный рабочий процесс: автоматически создаёт пулл реквесты для новых версий зависимостей и предупреждает об уязвимостях в зависимостях.
Dependabot позволяет настраивать частоту обновлений, метки, рецензентов и сообщения для коммитов, а также позволяет исключить зависимости, которые могут иметь критические изменения и конфликты при слиянии.
Включить обновления зависимостей можно в вашем репозитории на Github. Зайдите в Настройки > Безопасность и Анализ (Settings > Security & Analysis) и включите Обновления безопасности Dependabot (Dependabot security updates).

2. Renovate
Как и Dependabot, Renovate - это приложение GitHub, которое отслеживает ваши зависимости и открывает пул реквесты при появлении обновлений.
Главное преимущество Renovate в том, что он чрезвычайно настраиваемый. Он поддерживает обновления по расписанию, а также автоматическое слияние на основе правил, установленных в конфигурации.
Renovate также позволяет группировать обновления зависимостей в один пул реквест, а не создавать отдельные, что может значительно снизить трудозатраты для команды.

Анализаторы кода
3.
Snyk
Synk - платный продукт, но в личных проектах его можно использовать бесплатно. Это, по сути, набор продуктов для поиска и устранения уязвимостей в ваших зависимостях, коде и контейнерах.
Snyk использует семантический анализ для обнаружения ошибок безопасности и производительности. И в создаваемом им отчете для каждой уязвимости вы получите классификатор серьёзности, подробное объяснение проблемы, анализ исправления и способы предотвращения ошибки в будущем.

4. GitGuardian
GitGuardian - это, по сути, сервис для автоматического обнаружения и исправления секретной информации в вашем коде. Он интегрируется с Github и работает как с общедоступными, так и с частными репозиториями. GitGuarding также может быть интегрирован с рабочим процессом CI/CD для получения обратной связи по каждому коммиту. Еще одна важная функция - это интеграция со Slack/Discord, PagerDuty, которая позволяет разработчикам оставаться в курсе предупреждений системы безопасности.

5. Webhint
Webhint - это настраиваемый инструмент линтинга с открытым исходным кодом, который помогает улучшить доступность вашего сайта, скорость, кроссбраузерность, безопасность и т.д.

Источник: https://dev.to/opinionatedpie/5-developer-tools-for-detecting-and-fixing-security-vulnerabilities-h1j
День восемьсот шестьдесят второй. #Оффтоп #КакСтатьСеньором

Тестирование
Я настолько полюбил тестирование, что мне неловко писать код без тестов.

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

Я думаю о тестировании как о документировании. Это документация, подтверждающая мои предположения о коде. Тесты говорят мне, что я ожидаю от работы кода, и сообщают мне обо всех случаях, когда что-то идёт не так.

Итак, когда я пишу тесты, я:
1. Демонстрирую, как использовать класс/функцию/систему, которую я тестирую.
2. Демонстрирую всё, что может пойти не так.
3. В большинстве случаев тестирую поведение (публичный API), а не реализацию.

Всё, что я пропустил в п. 2, – это источники ошибок. Поэтому, всякий раз, когда я обнаруживаю ошибку, я убеждаюсь, что исправление кода имеет соответствующий тест, чтобы задокументировать: «вот ещё один способ, как что-то может пойти не так».

Просто написание этих тестов не улучшает качество моего кода, в отличие от написания самого кода. Но понимание, которое я получаю от чтения тестов, помогает мне писать лучший код.

Но это не единственный вид тестирования.

Есть машины, на которых вы разрабатываете (источник всех мемов «Оно работает на моей машине!»). Есть машины, на которых вы тестируете (зачастую, это те же машины для разработки). И наконец, есть машины, на которых вы развёртываете приложение. Если существует несоответствие среды между тестовой и производственной машинами, у вас будут проблемы.

У нас есть локальная разработка, например, в докере на машине разработчика. Есть среда разработки, где установлен набор библиотек (и инструментов разработки). Туда мы устанавливаем код, который мы пишем, и там же проводим все тесты с другими зависимыми системами. Затем идёт среда бета (staging), которая в точности совпадает с производственной средой. Наконец, производственная среда, состоящая из машин, на которых выполняется код и обслуживает реальных клиентов.

Идея состоит в том, чтобы попытаться выявить ошибки, которые не удалось бы выявить при модульном и системном тестировании. Например, несоответствие API запрашивающей и отвечающей системы.

Думаю, всё было бы по-другому в личном проекте или в небольшой компании. Не у всех есть ресурсы для создания собственной инфраструктуры. Однако эта идея актуальна для облачных провайдеров, таких как AWS и Azure.

Вы можете настроить отдельные кластеры для разработки и производственной среды. AWS ECS использует для развёртывания образы докеров, поэтому в разных средах всё относительно единообразно. Сложность - это интеграция с другими облачными сервисами: вызывать нужную конечную точку из правильной среды.

Источник: https://neilkakkar.com/things-I-learnt-from-a-senior-dev.html
Автор оригинала – Neil Kakkar
День восемьсот шестьдесят третий. #ЗаметкиНаПолях
Почему ASP.NET Приложение Такое Медленное? 10 Проблем с Производительностью и Их Решения
Проблемы с производительностью сервера могут возникать по разным причинам. Проблемы с памятью, медленные запросы к базе данных и слишком мало машин - вот лишь некоторые из них. В этой серии постов расскажу о 10 типах проблем, которые могут снижать производительность вашего сервера.

1. Медленные вызовы базы данных
Быстрое взаимодействие БД, вероятно, самое важное условие для хорошей производительности. По крайней мере, в большинстве приложений. К сожалению, есть много вещей, которые могут пойти не так, и даже невинно выглядящие реализации могут вызывать проблемы. Вот некоторые проблемы, которые могут приводить к медленным запросам к БД и снижать производительность вашего приложения:
- Плохой дизайн схемы
- Плохая стратегия индексации
и выполнение действий в приложении, а не в БД (см. производительность в EF Core).
- БД находится далеко от сервера.
Лучше всего разместить БД географически близко к вашим серверам, оптимально в том же центре обработки данных.

- Использование БД способом, для которого она не была создана.
Не все базы данных одинаковы. Некоторые из них идеально подходят для хранения пар «ключ-значение», другие - для транзакций, третьи по-прежнему идеально подходят для хранения журналов. Используйте лучшую БД для ваших нужд. Например, документоориентированная БД, вроде MongoDB, плохо справляется с операциями JOIN, и их использование может снизить производительность. Но она отлично подходит для хранения документов с большим количеством данных. Таким образом, одним из решений может быть дублирование информации между документами вместо использования JOIN.

- У БД недостаточно ресурсов.
Хотя очевидным решением будет масштабирование ваших серверов, не забывайте, что и базы данных должны масштабироваться. Например, в Azure SQL Server вам нужно будет следить за DTU (единицами транзакций базы данных). В других БД вам нужно следить за хранилищем, оперативной памятью, сетью и процессором. И не всегда система ясно предупреждает, когда вы приближаетесь к пределу. Всё просто начинает замедляться, и вы задаётесь вопросом, что, чёрт возьми, происходит.

- Неэффективные запросы всегда возможны. Если вы используете Entity Framework, сгенерированный SQL не всегда оптимален.
- Требуется каждый раз восстанавливать соединение. Если ваши соединения с БД не объединены в пул должным образом, возможно, соединение пересоздаётся для каждого запроса.
- Рассмотрите хранимые процедуры, когда сложный запрос занимает много времени.
- Плохая стратегия сегментирования.
Позаботьтесь о том, чтобы сгруппировать связанные данные в одном сегменте, иначе вы рискуете запрашивать несколько сегментов в одном запросе.

Самая сложная часть решения этих проблем - это изначально их идентифицировать. Есть много инструментов, чтобы увидеть, как ваши запросы работают в производственной среде. Обычно сама база данных может отображать отчёты о медленных запросах, проблемах с масштабированием, достижение сетью своих пределов и т.д. Решения вроде Application Insights очень хорошо это показывают. Также довольно просто добавить время выполнения запроса в журналы и автоматизировать отчёты на их основе.

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

Источник:
https://michaelscodingspot.com/slow-asp-net-server/
День восемьсот шестьдесят четвёртый.
Почему
ASP.NET Приложение Такое Медленное? 10 Проблем с Производительностью и Их Решения. Продолжение
Начало (1)

2. Давление на память (Memory pressure)
Одной из наиболее распространённых проблем на серверах с высокой нагрузкой является давление на память. В этом состоянии сборщик мусора не успевает частыми созданиями и уничтожениями объектов в памяти. Когда сборщик мусора находится под давлением, ваш сервер тратит больше времени на сборку мусора и меньше времени на выполнение кода.

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

Как и в случае с БД, самое важное - понять, что проблемы именно с памятью. Самый простой способ узнать это - использовать счетчики производительности.

Подробнее о работе с памятью.

3. Неоптимальный режим GC
Сборщик мусора .NET имеет два разных режима: режим GC рабочей станции и режим GC сервера. Первый оптимизирован для быстрого ответа с минимальным использованием ресурсов, а второй - для высокой пропускной способности.

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

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

Подробнее про сборщик мусора см. посты по тегу #GC.

4. Отсутствие кэширования
Кэширование может быть отличным методом оптимизации. Канонический пример: когда клиент отправляет запрос, сервер может сохранить результат в кэше. Когда клиент снова отправляет тот же запрос (это может быть другой клиент или тот же самый), серверу не нужно снова запрашивать базу данных или производить какие-либо вычисления для получения результата. Он просто извлекает его из кэша.

Простой пример этого - поиск чего-либо в Google. Если это что-то распространённое, то, вероятно, это запрашивают много раз в день. Нет смысла повторять всю магию Google, чтобы получить первую страницу с теми же 10 результатами. Её можно получить из кэша.

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

В ASP.NET, есть отличные реализации кеширования, которые делают большую часть работы за вас.

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

Источник:
https://michaelscodingspot.com/slow-asp-net-server/
День восемьсот шестьдесят пятый.
Почему
ASP.NET Приложение Такое Медленное? 10 Проблем с Производительностью и Их Решения. Продолжение
Начало (1)
Продолжение (2-4)

5. Ненужные клиентские запросы
Иногда удаётся значительно сократить количество клиентских запросов. Уменьшите это число, и вам потребуется будет меньше серверных машин или уменьшится нагрузка на существующие. Вот несколько способов сделать это:
- Тротлинг.
Рассмотрим механизм автозаполнения при поиске в Google. Когда вы начинаете вводить буквы, Google показывает раскрывающийся список с наиболее частыми поисковыми запросами, начинающимися с этих букв. Чтобы выдать эти данные автозаполнения, Google должен получить их с сервера. Допустим, вы набираете «Табуляция или пробелы». Google может отправить на свой сервер 15 запросов - на «Т», «Та», «Таб» и так далее. Но в этом нет необходимости. Лучше реализовать простой механизм тротлинга, который ожидает, пока вы не перестанете печатать, в течение 500 миллисекунд, а затем отправляет один запрос.
- Кэширование на стороне клиента.
Продолжая наш пример автозаполнения в поиске Google, есть много запросов, которые начинаются с одних и тех же слов. например, «Почему…», «Надо ли…», «Где…» и т. д. Вместо того, чтобы отправлять запросы для них каждый раз, Google может заранее сохранять наиболее распространённые результаты автозаполнения на стороне клиента, исключая ненужные запросы.
- Пакетная обработка.
Предположим на секунду, что Google шпионит за действиями пользователей, чтобы воспользоваться персонализированными данными… (предположим!). При использовании Gmail может потребоваться отправлять данные телеметрии каждый раз, когда вы читаете электронное письмо и наводите указатель мыши на определённое слово. Google может отправлять запрос для каждого такого случая, но будет более эффективным сохранить несколько таких случаев, а затем отправить их в одном запросе.

6. Зависание запросов
В определённых условиях запросы зависают. То есть вы отправляете запрос, но не получаете ответа. Или, скорее, вы в итоге получите ответ о тайм-ауте. Это может происходить, например, если возникла взаимная блокировка в коде, обрабатывающем запрос. Или, если у вас есть какой-то бесконечный цикл, который блокирует процессор. Или если вы бесконечно ждёте чего-то, что никогда не приходит - например, сообщения из очереди, длинного ответа базы данных или ответа от стороннего сервиса.

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

Таким образом, зависания запросов могут сильно сказаться на производительности вашего сервера, если они постоянно возникают.

Решение - устранить основную причину проблемы. Вам нужно сначала определить, что в реальности приводит к зависаниям, а затем предпринять шаги для их отладки.

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

Решение, конечно, в масштабировании. Это можно сделать двумя способами: вертикальное (также известное как scaling up - больше ядер CPU и памяти) и горизонтальное (также известное как scaling out - добавление большего количества машин). Облачные провайдеры обычно предлагают варианты простого автоматического масштабирования, которые стоит рассмотреть.

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

Источник:
https://michaelscodingspot.com/slow-asp-net-server/
День восемьсот шестьдесят шестой.
Почему
ASP.NET Приложение Такое Медленное? 10 Проблем с Производительностью и Их Решения. Окончание
Начало (1)
Продолжение (2-4)
Продолжение (5-7)

8. Сбои сервера
Как и зависания запросов, сбои сервера могут проявляться как проблемы с производительностью. Когда во время запроса происходит обычное исключение, приложение не вылетает. Сервер возвращает ответ об ошибке 500, и всё продолжается как обычно. Но сбой может произойти, если исключение случится вне контекста запроса, например, в потоке, который вы запустили сами. Помимо этого, существуют катастрофические исключения, такие как OutOfMemoryException, ExecutionEngineException и моё любимое StackOverflowException. Они приводят к сбою процесса независимо от того, сколько блоков catch вы разместите.

Когда приложение ASP.NET, размещённое в IIS, даёт сбой, сервер временно отключается. IIS выполняет перезапуск пула приложений, который перезапустит ваш сервер и вернётся к обычному режиму работы. Последствием для клиента будут временные медленные ответы или ошибки 503.

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

9. Чрезмерная функциональность для каждого запроса
Довольно часто ваши запросы украшают дополнительными функциями. Они могут иметь форму промежуточного ПО ASP.NET Core или фильтров действий. Эти функции могут включать в логирование, авторизацию, добавление заголовков ответов или что-то еще. Обратите особое внимание на эти фрагменты кода, потому что они выполняются для каждого запроса.

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

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

10. Синхронность против асинхронности
Когда ваш сервер отправляет запрос какому-то сервису и должен дождаться ответа, возникает риск. Что, если этот другой сервис занят обработкой большой очереди запросов? Что, если у него есть проблема с производительностью, от которой вам тоже придётся временно страдать?

Популярный способ решения этой проблемы - изменить синхронный вызов на асинхронный. Обычно это делается с помощью сервиса очереди, такой как Kafka или RabbitMQ. Вместо того, чтобы отправлять запрос и ждать ответа, вы отправляете сообщение в такую ​​очередь. Другой сервис извлекает эти сообщения и обрабатывает их.

А если вам нужен ответ, то сервис отправляет сообщение с ответом в ту же очередь. Вы также будете извлекать сообщения из очереди, и когда придёт ответ, сможете обработать его при необходимости, вне контекста исходного запроса. Если вам нужно отправить ответ клиенту, можно использовать push-уведомление, например, в SignalR.

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

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

Источник: https://michaelscodingspot.com/slow-asp-net-server/
👍1
День восемьсот шестьдесят седьмой.
Работа с Текстом в .NET с Помощью Humanizer
Для передачи сообщений в UI нам довольно часто требуется работать с текстом. NuGet пакет Humanizer серьёзно упрощает эту работу, позволяя выполнять десятки различных преобразований с помощью вызова методов расширения на строках и числах. Например:
"hello_world".Humanize(); // hello world
Помимо этого, методу можно передать значение перечисления LetterCasing и сделать заглавной первую букву строки, первую букву каждого слова или все буквы. При этом он не трогает популярные аббревиатуры, вроде HTML. Возможно и обратное преобразование из предложения в PascalCase строку.

Пока не сильно впечатляет? Подождите, вот лишь некоторые функции, которые мне показались наиболее интересными:
- Truncate(n) – обрезает строку до n символов и добавляет многоточие,
- Humanize(), применённая к значению enum, выдаёт либо предложение из PascalCase имени значения, либо значение атрибута Description, если он задан. Кроме того, если значение помечено атрибутом Display с данными локализации (Description и ResourceType), Humanize() выдаст локализованное описание.
- Humanize() на значении типа DateTime выдаст популярные в соцсетях строки о времени размещения поста: (минуту назад, час назад, вчера и т.п.) – всё относительно текущего времени.
- Humanize() на значении типа TimeSpan распишет период с заданной точностью, вроде: 2 недели, 3 дня, 6 часов и 24 минуты.
- ToQuantity(n) – выдаст строку вида «n штук» с правильным окончанием.
- Ordinalize() на целом значении выдаст порядковый номер, вроде 1ый, 2ой и т.п.
- ToWords() на числе выдаст число словами, например: сто двадцать три.
- ToOrdinalWords() на числе выдаст порядковый номер.
- ToOrdinalWords() на дате выдаст полное написание даты в текущей культуре, например, January 1st, 2015 для en-US.
- ToRoman() и FromRoman() – перевод из арабских чисел в римские и наоборот.
- Seconds(), Minutes(), Hours(),… позволяют использовать Fluent API для дат, например: DateTime.Now + 2.Days() + 3.Hours() - 5.Minutes()
- Bytes(), Kilobytes(), Megabytes(),… - аналогично позволяют переводить размеры в байтах, а Humanize() на результате выдаст «человеческое» представление размера, например:
(.5).Gigabytes().Humanize(); // 512 MB

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

Локализованные варианты пакета доступны более, чем на 15 языках, включая русский.

Подробнее читайте на страничке проекта в GitHub.

Источник: https://youtu.be/bLKXqJwRNSY
День восемьсот шестьдесят восьмой. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
88. Убунту-программирование
Часто мы пишем код в изоляции, и этот код отражает нашу личную интерпретацию проблемы, а также наше индивидуальное видение решения. Мы можем быть частью команды, но мы изолированы от команды. Мы слишком легко забываем, что этот код, созданный в изоляции, будет выполняться, использоваться, расширяться и использоваться другими. Легко упустить из виду социальную сторону создания программного обеспечения. Создание программного обеспечения - это техническая задача, смешанная с социальной задачей. Нам просто нужно чаще поднимать голову, чтобы понять, что мы не работаем изолированно, и мы несём общую ответственность за повышение вероятности успеха для всех, а не только для команды разработчиков.

Вы можете писать качественный код изолированно, всё время находясь в себе. С одной стороны, это эгоцентричный подход (эго в смысле эгоизма, а в смысле личности). Этого же придерживается Дзен: когда вы создаёте код, всё крутится вокруг вас. Я всегда стараюсь жить настоящим моментом, потому что это помогает мне писать более качественно, но при этом я живу в собственном моменте. А как насчёт момента моей команды? Мой момент совпадает с моментом команды?

На зулусском языке философия Убунту формулируется как «Umuntu ngumuntu ngabantu», что примерно переводится как «Человек - это личность в глазах (других) личностей». Мне становится лучше, потому что ты делаешь меня лучше. Обратной стороной является то, что ты становишься хуже в том, что делаешь, когда я плох в том, что я делаю. Что касается разработчиков, мы можем сузить эту формулировку до «Разработчик - это разработчик в глазах (других) разработчиков». Или совсем узко: «Код - это код с точки зрения (другого) кода».

Качество кода, который я пишу, влияет на качество кода, который вы пишете. Что делать, если мой код некачественный? Даже если вы пишете очень чистый код, именно в тех местах, где вы используете мой код, качество вашего кода упадёт до уровня, близкого к качеству моего кода. Вы можете применить множество схем и приёмов, чтобы ограничить урон, но ущерб уже нанесён. Я заставил вас сделать больше, чем нужно, просто потому что я не думал о вас, когда я проживал свой момент.

Я могу считать свой код чистым, но я всё ещё могу улучшить его при помощи Убунту-программирования. Как выглядит Убунту-программирование? Это похоже на создание хорошего чистого кода. Но дело не в самом коде. Дело в создании этого кода. Программирование для ваших коллег, согласно философии Убунту, поможет команде разделить ваши ценности и принципы. Следующий человек, который каким-либо образом коснется вашего кода, станет лучше, как человек и как разработчик.

Дзен – это про личность. Убунту - это дзен для группы людей. Мы очень редко мы создаём код только для самих себя.

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Aslam Khan
День восемьсот шестьдесят девятый. #ЗаметкиНаПолях
Различные Способы Проверки на Null в C#
В .NET существует несколько способов проверки на null. Но не все они равнозначны. Рассмотрим различные способы проверки:
object obj = ...;

// Основные проверки
obj == null;
object.Equals(obj, null);
object.ReferenceEquals(obj, null);

// Аналогичны ReferenceEquals(obj, null)
obj is null;
(object)obj == null;

// Аналогичны !ReferenceEquals(obj, null)
obj is not object;
obj is not {};

// Не работает, выдаёт NullReferenceException
obj.Equals(null);

Есть 3 основных способа:
1. object.ReferenceEquals(obj, null)
ReferenceEquals возвращает true, если экземпляры объекта являются одним и тем же экземпляром. В этом случае он возвращает true, если obj имеет значение null.

2. object.Equals(obj, null)
Equals аналогичен ReferenceEquals, когда один аргумент имеет значение null. Однако логика этого метода сложнее, чем ReferenceEquals, и немного медленнее, поскольку вызов метода не всегда встраивается.

3. obj == null
Оператор == аналогичен ReferenceEquals, если вы не определяете оператор равенства для класса. Если оператор определён, компилятор использует его вместо оператора по умолчанию. В этом случае логика сравнения настраивается, и она может и не учитывать проверку на null. Вы можете заставить компилятор использовать оператор ==, определённый для object, путём явного преобразования объекта к object.

Заметьте, что is null не работает со структурами, кроме обнуляемых:
MyStruct a = default;

// Ошибка компиляции
_ = a is null;

// Работает, только если MyStruct
// определяет оператор ==
_= a == null;

Span<char> span = default;

// Ошибка компиляции
_ = span is null;

// Работает, т.к. Span определяет оператор ==
_ = span == null;

int? b = null;
// Аналогично !b.HasValue
_ = b is null;

Заметьте, что хотя сопоставления с образцом is not null, is object и is {} аналогичны одной и той же проверке на null (см. выше), использование их вместе с объявлением переменной различается:
// Ошибка компиляции
if (obj is not null x) { … }
// OK, х – типа object
if (obj is object x) { … }
// OK, x – того же типа, что и obj
if (obj is {} x) { … }

Источник: https://www.meziantou.net/null-check-in-csharp.htm
День восемьсот семидесятый. #Оффтоп
Добрейшей пятницы. Пока желающие качают превью Visual Studio 2022, вот вам развлечение на это время.

Давненько я не рекомендовал интересных YouTube каналов. И вот на днях попался мне канал Dave’s Garage. А конкретнее, видео «Software Drag Racing: C++ vs C# vs Python - Which Will Win?» Какой язык программирования быстрее? Что может быть более холиварной темой?

Автор канала – David Plummer, бывший инженер операционных систем в Microsoft, работавший ещё аж над MS DOS и Windows 95 (в частности, он создатель Диспетчера задач Windows). Так вот, он решил проверить, какой язык быстрее справится с алгоритмом нахождения всех простых чисел меньше заданного.

В видео рассказывается про собственно алгоритм (сильно оптимизированный, байто…черы оценят), его реализацию на Python, C# и C++, ну и, собственно, бенчмарк в виде количества выполнений алгоритма за 5 секунд.
Результаты спойлерить не буду, смотрите видео.

Кстати, в GitHub Дейва есть реализации алгоритма чуть менее, чем на всех популярных языках, с дальнейшими оптимизациями от контрибуторов, многократно превосходящими по производительности результаты из видео.

А вообще, канал очень понравился. Олдам зайдут и другие видео с байками, вроде Почему возникает синий экран смерти, и почему он синий, Почему перезагрузка решает все проблемы (и при чём тут ботинки) или Секретная история активатора Windows.

Единственное замечание: требуется хороший уровень восприятия американского английского, потому что автор говорит довольно быстро и не всегда чётко.
День восемьсот семьдесят первый.
Странное Разрешение Имён Перечислений в C#
Рассмотрим следующий код:
using System;
namespace Test
{
enum Foo {
A = 1, B = 1, C = 1
}

public static class Program
{
public static void Main()
{
Console.WriteLine("{0}, {1}, {2}",
Foo.A, Foo.B, Foo.C);
}
}
}

Странно, но этот код выдаёт строку B, B, B. Это поведение одинаково и в .NET Framework, и в .NET Core 3.x, и в .NET 5.

Почему он выбирает B?

Согласно документации для Enum.GetName():
Если несколько членов перечисления имеют одно и то же значение, метод GetName гарантирует, что он вернёт имя одного из этих членов перечисления. Однако он не гарантирует, что всегда будет возвращать имя одного и того же члена перечисления.

Так что формально могло бы возвратиться что угодно. Почему же всё-таки возвращается B?

За объяснением можно обратиться к реализации GetEnumName().

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

Источник: https://stackoverflow.com/questions/67091644/weird-enum-name-resolution-in-c-sharp
День восемьсот семьдесят второй. #Оффтоп #КакСтатьСеньором

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

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

Конечно, просто собрать требования - это еще не всё, о чем стоит подумать. Включение процессов разработки в проектирование также окупается:
- Как будет организовано написание кода, сборка, развёртывание и CI/CD?
- Как мы будем проводить сквозное и нагрузочное тестирование?
- Как мы будем управлять секретами?

Например, кто бы мог подумать, что управление секретами в производственной среде может оказаться таким сложным? Вы не можете поместить их в код, так как тогда их сможет увидеть любой.
Поместить их в переменные среды? Хорошая идея. Как их туда поместить и как ими управлять? Хранить файл секретов? Откуда он будет браться и как его изменять? И т.д., и т.п. Кроме того, мы не хотим, чтобы всё это выполнялось вручную. Можно рассмотреть базу данных, чтобы код получал секреты из базы при запуске. Опять же, подход может сильно отличаться при использовании облачного провайдера, где не нужно много думать о секретах, т.к. для этого есть специальный функционал.

Проектирование с учетом поддержки
Проектировать системы - это увлекательно. Поддерживать их – не очень. Рано или поздно задаёшься вопросом, почему и как системы деградируют?

Во-первых, проблема в нежелании выбрасывать старые вещи, всегда добавляя новые. Склонность к добавлению вместо удаления (есть за вами такой грешок?)

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

Есть как минимум три способа снизить скорость деградации:
1. Разделяйте бизнес-логику и инфраструктуру. Обычно быстрее деградирует инфраструктура: увеличивается нагрузка, фреймворки устаревают, появляются уязвимости и т.д.
2. Стройте процессы вокруг поддержки. Обновляйте как новый, так и устаревший код. Это предотвращает различие между новыми и старыми частями и сохраняет весь код «современным».
3. Убедитесь, что вы на постоянной основе избавляетесь от всех ненужных/устаревших вещей.

Источник:
https://neilkakkar.com/things-I-learnt-from-a-senior-dev.html
Автор оригинала – Neil Kakkar
День восемьсот семьдесят третий.
RestClient.Net 5
RestClient.Net упрощает HTTP-вызовы в .NET. Вы можете отправлять строго типизированный объект в теле запроса и получать обратно строго типизированный объект. Вы можете внедрить абстракцию в сервисные классы и легко имитировать его в тестах, не беспокоясь о подключении HTTP или преобразовании в JSON. RestClient.Net 5 значительно улучшен, относительно 4й версии: представлены неизменяемые типы и fluent API. Он прекрасно работает с внедрением зависимостей, интегрируется с Polly, а дизайн должен быть знаком и удобен программистам на F#.

Зачем использовать Rest Client?
HttpClient значительно затрудняет модульное тестирование вашего кода. Прежде всего, HttpClient не имеет простой абстракции. Если вы хотите имитировать HttpClient для модульного тестирования, вам необходимо внедрить DelegatingHandler в клиента. Даже в этом случае вам нужно имитировать преобразование в необработанные данные и обратно. Это отвлекает от простого действия по проверке того, что клиент отправляет и получает правильный объект.

RestClient.Net решает эту проблему с помощью интерфейса IClient. Вы отправляете строго типизированный объект и в теле ответа получаете строго типизированный объект.
using var client = new Client(
"jsonplaceholder.typicode.com".ToHttpsUriFromHost());
UserPost userPost =
await client.PostAsync<UserPost, UserPost>(
new UserPost { title = "Title" }, "posts"
);

Внедрение зависимости
Вы можете напрямую внедрить интерфейс IClient или использовать фабричный подход с CreateClient. Пакет RestClient.Net.DependencyInjection работает с внедрением зависимостей ASP.NET Core и использует реализацию IHttpClientFactory по умолчанию для создания HttpClient. Это означает, что RestClient.Net всегда получает HttpClient через фабрику, которая поддерживается Microsoft.

Url-адреса
RestClient.Net использует библиотеку Urls вместо System.Uri. Это упрощает создание URL-адресов, делает их более читаемыми и менее подверженными ошибкам. Добавление пути и строки запроса к базовому URL-адресу всегда безопасно, и вам не нужно беспокоиться об объединении строк вручную:
var absoluteUrl =
Host.ToHttpUrlFromHost(Port)
.AddQueryParameter(FieldName1, FieldValue1)
.WithCredentials(Username, Password)
.AddQueryParameter(FieldName2, FieldValue2)
.WithFragment(Fragment)
.WithPath(PathPart1, PathPart2);
Этот подход я подробнее описывал для пакета Flurl.

Неизменяемые типы
HttpClient позволяет изменять множество свойств. Это означает, что, если несколько частей вашей системы используют один и тот же экземпляр, они могут изменить какое-то свойство и сломать другую часть системы. Например, один класс может добавить заголовок по умолчанию, а другой класс может удалить его.
Все классы в RestClient.Net неизменяемы. Вы не можете изменить их значения после создания экземпляра. Вы можете быстро клонировать существующего клиента, используя методы With или использовать параметры фабрики CreateClient, чтобы указать свойства клиента.

Кроме того
- RestClient.Net поддерживает .NET Framework 4.5 и .NET Standard 2.0.
- Тщательно протестирован с помощью Stryker Mutator.
- Простой в использовании и легко масштабируется. Вы можете выполнить HTTP-вызов в одну строку кода, а затем повторно использовать клиента и выполнять последующие вызовы, задавая новые URL.

Источник: https://christianfindlay.com/2021/05/26/restclient-net-5/
День восемьсот семьдесят четвёртый.
Ответы на Самые Популярные Вопросы по Микросервисам в .NET
1. При масштабировании сервисов как мы можем масштабировать базы данных, связанные с этими сервисами?
Существуют чётко определенные шаблоны и передовые методы повышения производительности и масштабирования баз данных. Обратитесь к разделу «Горизонтальное, вертикальное и функциональное разбиение данных», чтобы понять, как данные делятся на разделы для повышения масштабируемости, уменьшения числа конфликтов и оптимизации производительности. Чтобы углубиться в темы масштабирования микросервисов, распределённых данных, подхода «база данных на микросервис», выбора между реляционными или NoSQL базами данных, обратитесь к книге «Архитектура облачных приложений .NET для Azure» (PDF).

2. Нужно ли использовать отдельную базу данных для каждого микросервиса или микросервисы могут совместно использовать один и тот же экземпляр базы данных?
Автономность команд при работе со своими микросервисами - важнейшее преимущество архитектуры облачных приложений. Предпочтительно использовать независимые экземпляры базы данных, чтобы дать командам гибкость при развёртывании обновлений и исправлений в производственной среде, не нарушая работу других микросервисов. Архитектура облачных приложений вдохновлена известными методологиями 12-факторных приложений https://12factor.net/ru/. Один из факторов, «Сторонние сервисы», гласит, что вспомогательные ресурсы, такие как хранилища данных, кэши, брокеры сообщений, должны быть доступны через адресный URL. Поставщики облачных услуг предлагают широкий ассортимент управляемых сторонних сервисов.

3. Может ли монолитный веб-API взаимодействовать с микросервисами?
Да. Монолитные приложения могут взаимодействовать с микросервисами, если их конечные точки доступны внутри инфраструктуры или публично через безопасный протокол. Микросервисы и их данные могут использоваться либо синхронно через их конечные точки, либо асинхронно через обмен сообщениями, например шину событий. При модернизации приложения мы рекомендуем паттерн «Душитель» (Strangler), который помогает постепенно переходить с устаревшей системы на новую. В рамках решения вам необходимо создать фасад, который перехватывает запросы. Фасад направляет эти запросы либо в унаследованное приложение, либо в новые сервисы.

4. Если микросервисы слабо связаны и развертываются независимо, как они взаимодействуют друг с другом? Как синхронизировать данные между микросервисами?
Это довольно объёмный вопрос. Он подробно разъясняется в двух разделах книги «Архитектура облачных приложений .NET для Azure»:
- Модели связи в облаке
- Распределённые данные

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

Больше вопросов и ответов в видео Microsoft Let’s Learn: Microservices.

Источник: https://devblogs.microsoft.com/aspnet/your-top-dotnet-microservices-questions-answered/
День восемьсот семьдесят пятый.
ASP.NET Core 6 и Серверы Аутентификации
Начиная с .NET 3.0 IdentityServer4 поставляется как часть шаблона приложения для поддержки выпуска токенов JWT в приложениях SPA и Blazor. Через некоторое время выпуска продукта команда IdentityServer сделала объявление об изменении лицензии для будущих версий IdentityServer на взаимную общественную лицензию (RPL), в которой код по-прежнему является открытым, но, если он используется в коммерческих целях, необходимо покупать платную лицензию. Такой подход распространён в мире открытого исходного кода, где трудно поддерживать доход, поскольку ваш проект становится вашей работой на полную ставку.

Причинами для выпуска IdentityServer было четко выраженное желание сообщества не конкурировать с существующим проектом с открытым исходным кодом и глубокое понимание командой IdentityServer сферы идентификации. Команда .NET не является экспертами по OAuth и OIDC, поскольку они сосредоточены на предоставлении строительных блоков для вашего приложения. Создание и поддержка сервера аутентификации - это полноценная задача. И у Microsoft уже есть команда и продукт в этой области - Azure Active Directory, который позволяет бесплатно использовать 500 000 объектов. Команда ASP.NET считает, что управляемое облачное решение остается лучшим практическим вариантом для разработчиков: безопасность поддерживается, учётные данные не хранятся локально, что сопряжено с риском, а новые функции, такие как аутентификация без пароля, легко внедрить в рабочий процесс. Однако понятно, что облачное решение может быть невозможно для некоторых клиентов из-за проблем с нормативными требованиями или суверенностью данных.

Для .NET 6 Microsoft продолжит поставлять IdentityServer в шаблонах проектов, используя новую лицензию RPL. IdentityServer по-прежнему рассматривается как наиболее зрелый вариант для создания самостоятельно развёртываемой, локально размещённой службы токенов в ASP.NET Core. Требования к лицензированию будут отдельно описаны, если вы используете шаблон, который включает Duende IdentityServer. Новый Duende IdentityServer по-прежнему имеет открытый исходный код, но теперь имеет двойную лицензию. Эта лицензия позволяет использовать его бесплатно для разработки, тестирования и обучения, бесплатно для некоммерческих программ с открытым исходным кодом и бесплатно для использования в коммерческих условиях, если компания зарабатывает менее 1 миллиона долларов США в год. В других случаях лицензия требует платы за использование в коммерческих целях. Предыдущая версия IdentityServer будет по-прежнему поддерживаться, пока поддерживается .NET 5, примерно до февраля 2022 года.

Для .NET 7 вопрос создания инструментов, позволяющих разрабатывать и тестировать приложения с поддержкой OIDC (OpenID Connect), пока открыт. Вы всегда сможете выбрать любую систему идентификации, которая лучше всего подходит для вашей среды, обновив всего несколько строк кода.

Про лицензирование IdentityServer, кстати, говорили в недавнем выпуске подкаста .NET Rocks «Open Source in the Enterprise with Rocky Lhotka». Вообще обсуждалась проблема использования открытого кода в энтерпрайз приложениях. Какие риски возникают в связи с этим, как правильно использовать открытые лицензии, и что будет, если, к примеру, какой-то из используемых модулей перестанет поддерживаться (как это случалось неоднократно). В разрезе IdentityServer, по словам авторов подкаста, история была в том, что компании просили сделать платную лицензию, чтобы была постоянная возможность обращаться в техподдержку, а у создателей IdentityServer соответственно обязательство оперативно решать проблемы клиентов, которого не может возникать при использовании открытого кода.

Источник: https://devblogs.microsoft.com/aspnet/asp-net-core-6-and-authentication-servers/
День восемьсот семьдесят шестой. #ЧтоНовенького #ЗаметкиНаПолях
Работа с PriorityQueue в .NET 6
Я недавно писал про новый класс PriorityQueue, предложенный в .NET 6.
В отличие от обычной очереди, работающей по принципу FIFO (первым пришёл - первым ушёл), очередь с приоритетом не имеет красивого акронима. Заданный пользователем приоритет определяет, какой элемент является «первым». PriorityQueue определяет приоритет значений с помощью реализации интерфейса IComparer.

Очереди с приоритетом имеют много применений, но чаще всего их можно увидеть при работе с «обходом графа», поскольку вы можете быстро идентифицировать узлы, которые имеют самую высокую, либо самую низкую «стоимость».

PriorityQueue принимает два аргумента типа: тип значения и тип приоритета. Конструктор также может принимать экземпляр IComparer, но это не обязательно, поскольку большинство примитивных типов в .NET уже имеют реализацию компаратора по умолчанию. Давайте рассмотрим небольшой пример. Мы добавим супергероев в новую PriorityQueue по их величию:

PriorityQueue<string, Greatness> heroes 
= new(new GreatnessComparer());

heroes.Enqueue("Captain America", GOAT);
heroes.Enqueue("Spider-Man", Great);
heroes.Enqueue("Dr. Strange", Good);
heroes.Enqueue("Thor", Great);
heroes.Enqueue("Iron Man", Ok);
heroes.Enqueue("Hulk", Good);

while (superheroes.TryDequeue(out var hero, out var greatness))
Console.WriteLine($"{hero} ({greatness})");

Здесь величие определяется в перечислении
public enum Greatness
{
Ok, Good, Great, GOAT
}
Для него реализован компаратор:
public class GreatnessComparer 
: IComparer<Greatness>
{
// от большего к меньшему
public int Compare(Greatness x, Greatness y) => y - x;
}

PriorityQueue будет определять приоритет наших супергероев, используя реализацию интерфейса IComparer. Запустив программу, мы должны увидеть следующий вывод:
Captain America (GOAT)
Spider-Man (Great)
Thor (Great)
Hulk (Good)
Dr. Strange (Good)
Iron Man (Ok)

Заметьте, что элементы с одинаковым приоритетом не следуют порядку FIFO. На самом деле, порядок их выдачи не определён, т.к. «под капотом» в PriorityQueue лежит дерево элементов.

Источник: https://khalidabuhakmeh.com/working-with-dotnet-six-priorityqueue