.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
День пятьсот девяносто седьмой. #MoreEffectiveCSharp
17. Избегайте Возврата Ссылок на Внутренний Класс
Вы можете думать, что свойство только для чтения доступно только для чтения и вызывающие объекты не могут его изменять. К сожалению, так получается не всегда. Если свойство возвращает ссылочный тип, вызывающий код может получить доступ к любому общедоступному члену этого объекта, включая те, которые изменяют его состояние. Например:
public class MyObject {
public MyObject() {
Data = new List<ImportantData>();
}
public List<ImportantData> Data { get; }

}
Получаем доступ к списку и удаляем его элементы. Это поведение не предусмотрено, но и не запрещено:
var stuff = obj.Data;
stuff.Clear();
Таким образом свойство только для чтения - дыра в продуманной инкапсуляции вашего класса. Не говоря уже о том, что создатель класса рассматривает это свойство как неизменяемое и не ожидает подвоха.

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

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

1. Типы значений копируются, когда клиенты обращаются к ним через свойство. Любые изменения копии, полученной клиентами вашего класса, не влияют на внутреннее состояние вашего объекта.

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

3. Интерфейсы позволяют клиентам получать доступ к подмножеству функционала вашего внутреннего члена. Предоставляя функциональность через интерфейс, вы сводите к минимуму вероятность того, что ваши внутренние данные изменятся не так, как вы предполагали. Клиенты могут получить доступ к внутреннему объекту через предоставленный вами интерфейс, который не будет включать в себя все функции класса. В нашем случае публичное свойство можно представлять клиентам в виде IEnumerable<T> вместо List<T>.

4. Последний вариант – предоставить объект-оболочку, минимизирующую варианты доступа к содержащемуся объекту. В .NET представлены различные типы неизменяемых коллекций, которые это поддерживают. Тип System.Collections.ObjectModel.ReadOnlyCollection<T> - это стандартный способ обернуть коллекцию для предоставления во вне данных только для чтения:
public class MyObject {
private List<ImportantData> listOfData =
new List<ImportantData>();

public ReadOnlyCollection<ImportantData> Data =>
new ReadOnlyCollection<ImportantData>(listOfData);

}

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

Источник: Bill Wagner “More Effective C#”. – 2nd ed. Глава 17.
День пятьсот девяносто восьмой. #Оффтоп #ЗадачиНаСобеседовании
Как насчёт задачки на выходные?)))
Думаю, большинство застало кнопочные телефоны с буками. А олды ещё помнят, что буквы были и на стационарных телефонах, особенно импортных (см. картинку).
На самом деле это довольно популярная система в основном в США для запоминания «красивых» корпоративных номеров. Номеров типа 8-800-700-8000 (кстати, это техподдержка Билайна, не спрашивайте, почему я его помню) на всех не хватит. Тогда придумали расположить алфавит на кнопках, и клиент должен был просто нажимать кнопки, соответствующие буквам. Например:
1-800-flowers означало номер 1-800-3569377
Сам номер «некрасивый», а слово легко запомнить.

Итак, к задаче. Дан номер телефона, например 3662277 и список слов, например ["foo", "bar", "baz", "foobar", "cap", "car", "cat"].
Нужно определить, какие слова из списка содержатся в номере телефона. Номер телефона может быть разной длины, но в разумных пределах. Слова должны содержаться целиком как подстроки.
День пятьсот девяносто девятый. #Оффтоп
Чистый Код
Воскресное ненапряжное – сказка от дядюшки Боба. Видео древнее, как Windows XP, но почему-то раньше мне не попадалось.

В этом видео дядя Боб показывает, почему так важен чистый код, объясняет, как плохой код ведет к нисходящей спирали «Ловушки производительности», а также описывает разные формы «гниения кода».

В общем, «Чистый код» и «Мифический человеко-месяц» в одном часовом видео с шутками, прибаутками и отсылками к «Звёздным войнам», «Шерлоку», «Стар-треку» и куче всего другого. Энджой!

https://www.youtube.com/watch?v=Wibk0IfjfaI
День шестисотый.
Очередной юбилей, 600 дней дневнику.
Сегодня немного расскажу о текущем положении вещей в проектах.
Я продолжаю подготовку к экзамену 70-486. Приблизительно наметил дату сдачи на конец октября – начало ноября. Я в принципе прошёл по темам экзамена, осталось закрепить материал, особенно по Azure. Поэтому решил пройти несколько курсов на Pluralsight:
- ASP.NET Core
- .NET Developer on Microsoft Azure
- Microsoft Azure Compute for Developers
- Continuous Delivery and DevOps With Azure DevOps
Кроме того, на Microsoft Learn нашёл несколько интересных курсов:
- Deploy a website to Azure with Azure App Service
- Migrate an ASP.NET web application to Azure with Visual Studio
- Work with relational data in Azure
Не уверен, что посмотрю всё, но попробую. Может и кому из вас пригодится.
Кроме того, как я недавно писал, в конце сентября будет онлайн курс «Modernizing Web Applications and Data».


Что касается проекта «Путь разработчика». Спасибо всем за предложения в issues. Большинство из них были учтены и добавлены в описание. Кроме того, возникла идея реализовать Чистую архитектуру и паттерн CQRS. Кому интересно, вот тут составил плейлист по этим темам (извините, видео только на английском). За идею спасибо участникам чата. Они, кстати, решили написать другой проект по автоматизации системы учёта потребления газа. И поскольку старт моего проекта немного затягивается, кому не терпится попробовать свои силы, добро пожаловать. Публикую ссылку на группу с их разрешения. Многие идеи и технологии разработки и менеджмента проекта будут опробованы там и взяты в наш проект.
День шестьсот первый. #Оффтоп #ЗадачиНаСобеседовании
Ответ на задачу про телефонные номера

На первый взгляд, всё довольно просто. Сначала для удобства представим все слова в виде соответствующих цифр. Таким образом, вместо
["foo", "bar", "baz", "foobar", "cap", "car", "cat"]
у нас получится
["366", "227", "229", "366227", "227", "227", "228"]
Это можно сделать, например, через функцию маппинга. Составим массив из 26 цифр, соответствующих каждой букве на кнопке:
int[] map = {2,2,2,3,3,3,4,4,4,…};
Затем в цикле проходим по буквам в слове, ищем соответствующую цифру и добавляем в результирующий массив:
int[] res = new int[word.Length];
for (int i = 0; i < res.Length; i++)
res[i] = map[word[i] - 'a'];

Дальше интереснее.

Простой вариант
Для каждой цифры номера телефона составим древовидную структуру из цифр, где корень – текущая цифра телефона, а потомки – все цифры после неё. Таким образом, для числа из задачи 3662277 у нас получится нечто подобное:
3-6-6-2-2-7-7
6-6-2-2-7-7
6-2-2-7-7
2-2-7-7
2-7-7
7-7
Одинаковые корни объединим. То есть, например, у корня 2 будет 2 ветки, начинающиеся на 2 и 7:
2-2-7-7
\
7-7

Теперь для каждого слова мы будем по порядку его символов (цифр) проходить по этому дереву и искать, есть ли узлы со значением текущего символа слова. Например, для "bar" => 227:
- Корень – 2 – есть,
- Дочерний узел – 2 (2-2) – есть,
- Дочерний узел – 7 (2-2-7) – есть,
- Слово закончилось, значит это слово содержится в исходном номере.

Для "map" => 627:
- Корень – 6 – есть,
- Дочерний узел – 2 (6-2) – есть,
- Дочерний узел – 7 (6-2-7) – нет, значит такого слова нет.

Вариант "посложнее"
Как вы понимаете, этот алгоритм не слишком эффективный. Хотя, для цифр это ещё куда ни шло, а представьте размер дерева из всех возможных символов. Для таких случаев в 1975 году изобретён алгоритм Ахо—Корасик. Суть его в том, что дерево строится не на основе исходной строки, а специальным образом на основе искомых строк с прямыми и обратными ссылками на узлы. Если интересно и хочется поломать голову, можете почитать о нём. Алгоритм позволяет производить поиск подстрок за линейное время.

«Кандидат» на видео (ссылка ниже) о нём знал (не зря работает в Facebook), и ему даже приходилось его реализовывать. Но не пугайтесь, от вас на собеседовании этого вряд ли потребуют))).

Источник: https://www.youtube.com/watch?v=PIeiiceWe_w
День шестьсот второй. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
60. Работайте по Правилу 80/20
Этот принцип изменит то, как вы принимаете решения.

«Принцип Парето (также известный как правило 80/20) утверждает, что 20% усилий дают 80% результата, а остальные 80% усилий — лишь 20% результата». - Википедия

Строго говоря «80%» здесь означает большую часть, а «20%» - меньшую. Таким образом, хотя проценты могут варьироваться от случая к случаю, этот подход всё равно справедлив.

1. Когда работать
Большинство программистов совершают большую ошибку: они работают с 9 до 5 в офисе несмотря на то, что им предлагается более гибкий график. Они считают, что именно так они могут быть продуктивными, даже если устают после обеда. Вот как может помочь правило 80/20: поймите, что 80% вашей работы будет выполнено за 20% времени. Лично ваше самое продуктивное время вполне может быть с 5 до 9 утра. Следуя норме, вы никогда не узнаете, как много работы вы можете делать всего за несколько часов.

2. При выборе функционала
Большинство ваших пользователей будут использовать только 20% функций, которые вы им даёте. Таким образом, нужно вложить 80% усилий в эти функции и сделать их основой вашего продукта.

3. При сортировке списка дел
Многие из нас составляют списки дел, чтобы завершать наши рабочие задачи. В большинстве случаев 20% вашего списка отнимут 80% вашего времени. Таким образом, сортировка списка может помочь вам быстрее выполнить больше задач или сначала закончить самую важную часть. Сортировка списка в соответствии с этим правилом поможет вам дольше сохранять мотивацию и оценивать, сколько времени займет выполнение вашего списка дел.

4. При запуске проекта
80% времени, затрачиваемого на проект, следует посвящать первым 20% работы над ним. Мозговой штурм, продумывание структуры проекта и планирование задач помогут проекту продвигаться быстрее и легче. Поэтому, прежде чем бросаться писать код, обязательно вложите достаточно усилий в первые 20% своего проекта.

5. При изучении нового
Вам нужно выучить только 20% чего-либо, чтобы начать пользоваться этим. Допустим, вы хотите изучить новый язык. Выбор правильных 20% материала (например, принципов ООП или функционального программирования, синтаксиса языка и т. д.) и хорошее понимание этой части поможет вам изучить остальное быстрее. Когда вы освоите эти принципы, вы сможете уверенно приступить к программированию - даже если вы ещё не знаете оставшихся 80%!
Кроме того помните, что, усвоив 20% материала любой книги, курса или руководства, вы будете знать на 80% больше, чем те, кто этот материал не изучал вообще.

6. При отладке
Отладка может занимать часы. Многие разработчики говорят, что 80% их ошибок находятся в 20% кода. Поэтому разумно потратить больше времени на исправление, когда вы находите ошибку, потому что в том же фрагменте кода может быть больше ошибок.

7. При выборе идеи
Если вы разработчик, у вас могут появляться сотни идей для нового приложения. Поэтому знание того, что лишь 20% ваших идей достойны воплощения и будут работать, может помочь вам выбрать, какую идею лучше всего реализовать. Не пытайтесь реализовать всё.

Источник: https://medium.com/better-programming/change-your-life-as-a-programmer-with-the-80-20-rule-17c325609343
Автор оригинала - Nuha Khaled
День шестьсот третий. #Оффтоп
Заменит ли WebAssembly Javascript?
В зависимости от того, с кем вы разговариваете, можно услышать мнения, что WebAssembly либо:
- убьёт Javascript,
- улучшит Javascript,
- останется нишевой технологией небольшой группы разработчиков, предпочитающих работать с компилируемым языком.

Была проделана большая работа по снижению проблем с производительностью, которые преследовали WebAssembly при выполнении JS-вызовов (особенно при работе с DOM). Похоже, что усилия сосредоточены на том, чтобы сделать WebAssembly не просто компактной сборкой кода, а жизнеспособной альтернативой.

Дело в том, что приложениям WebAssembly, которым нужен доступ к DOM, всё равно приходится использовать Javascript. Насколько мне известно, пока нет планов разрешить прямой доступ к DOM через WebAssembly.

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

Новой функцией, которая будет встроена в WebAssembly, является сборка мусора, и к ней можно будет получить доступ из Javascript. Хотя Javascript отлично подходит для создания UI веб-приложений, вы можете попасть в ловушку производительности при создании приложений, интенсивно использующих GPU/CPU. Интересной областью применения является низкоуровневая графика, позволяющая таким вещам, как виртуальная реальность в браузере, достигать требуемой производительности и высокого фреймрейта, необходимых для бесперебойной работы.

Одна из областей, в которой WebAssembly может быть использована, - это игры. Перенос Doom 3 в WebAssembly оказался огромным успехом. Известно, что Figma сделала первый шаг к использованию WebAssembly ещё в 2017 году и увидела огромные преимущества в производительности.

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

Также WebAssembly может использоваться в веб-фреймворках и библиотеках для виртуальных реализаций DOM, освобождая основной поток для UI и обеспечивая более плавную работу графики.

Как видите, многие из возможных вариантов использования WebAssembly всё равно будут взаимодействовать с UI, и здесь CSS, HTML и Javascript по-прежнему будут необходимы для создания богатых интерфейсов. Но WebAssembly позволит разработчикам перенести интенсивную работу, блокирующую UI, за пределы браузера, и это приведет к более плавной работе веб-приложений.

Итак, отвечая на изначальный вопрос, WebAssembly не заменит Javascript. WebAssembly сделает Javascript лучше.

Источник: https://ilikekillnerds.com/2020/09/will-webassembly-replace-javascript/
День шестьсот четвёртый. #Оффтоп
Пишите Тесты Быстрее с Approval Tests
Иногда проверочная (assert) часть в тестах становится большой, беспорядочной и сложной, для понимания, если нам приходится тестировать сложный результат, который выдаёт метод.

Approval Tests - это библиотека, которая может помочь упростить код проверки. Допустим, у нас есть простой генератор отчётов, и мы хотим проверить, что он правильно форматирует отчёт. Рассмотрим следующий тестовый метод:
[Fact]
public void ReportGeneratesCorrectly() {
var lines = new List<string> {
"Line 1", "Line 2", "Line 3"
};

var sut = new ReportGenerator(
title: "Annual Report",
footer: "Copyright 2020",
lines);

string report = sut.Generate();

var results = report.Split(Environment.NewLine);
Assert.Equal("Annual Report", results[0]);
Assert.Equal("Line 1", results[2]);
Assert.Equal("Line 2", results[3]);
Assert.Equal("Line 3", results[4]);
Assert.Equal("Total lines: 3", results[6]);
Assert.Equal("Copyright 2020", results[8]);
}

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

В этих случаях Approval Tests помогут упростить код проверки до единственной строки:
Approvals.Verify(report);

Эта строка вызывает Approval Tests и создаёт в тестовом проекте файл <имя_теста>.received.txt. Вы можете проверить этот файл и, если он верен, переименовать его в <имя_теста>.approved.txt. Когда тест будет запущен в будущем, Approval Tests будут использовать файл approved (который должен быть добавлен в систему управления версиями), и если сгенерированный результат будет таким же, то тест будет пройден, в противном случае тест завершится неудачей, и будет создан новый файл received.

Атрибут [UseReporter] тестового метода позволяет вам указать, как визуализировать ошибки проверки, например, через инструмент diff или ряд других:
[UseReporter(typeof(DiffReporter))]

Если вы хотите подробнее узнать про Approval Tests, посмотрите курс «Approval Tests for .NET» на Pluralsight.

Источник: https://dontcodetired.com/blog/post/Approval-Tests-Write-Tests-More-Quickly
День шестьсот пятый. #ЧтоНовенького #CSharp9
C#9: Улучшенное Сопоставление с Образцом
В своё время VB стал популярным из-за своей выразительности. Программу можно было читать почти как обычный английский текст. Для сравнения, C# и C++ оптимизированы для краткости синтаксиса и производительности. Это сделало их быстрыми, но в некоторых случаях трудным для чтения. Сравните VB:
If Not a And b Then

Else

EndIf

И C#:
if(!(a) && b)
{

}
else
{

}

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

C#9 теперь поддерживает несколько новых ключевых слов: not, and и or. Это упрощает чтение некоторых выражений, но вы по-прежнему можете использовать более короткий синтаксис с использованием символов:
if(s is not string)
на мой взгляд, проще для чтения, чем
if(!(s is string))

Сопоставление с образцом
Эти новые операторы полезны при сопоставлении с образцом. Кстати, теперь вы также можете использовать реляционные шаблоны, такие как >=, > и т.д., непосредственно в выражениях:
record Person(int? Weight);
Person p = new Person(175);
var category = p.Weight switch
{
< 150 => "лёгкий",
>= 150 and < 200 => "средний",
not null => "неизвестно",
null => "ошибка"
};

До C#9 некоторые шаблоны было невозможно выразить, например, not null. В основном это было ограничением компилятора, потому что !null не был допустимым синтаксическим токеном в C#. То же самое относится к таким выражениям, как
>= 150 and < 200
До C#9 компилятор не знал, как анализировать &&, за которым следует другой логический символ, например <. В выражении сопоставления с образцом компилятор знает, с какой переменной мы сопоставляем, и понимает, что означает это выражение, без повторяющихся объявлений переменных, которые только загромождали выражение. В итоге мы получили синтаксис, который намного проще понять людям, читающим код.

Источник: https://blog.miguelbernard.com/c-9-0-improved-pattern-matching/
День шестьсот шестой. #юмор
Какие виды программистов бывают? Шарписты, джависты, сишники? Бэкэндеры, фронтендеры, фулл-стек? Джуны, мидлы, сеньоры? На самом деле есть и другое деление, не зависящее от языка, стека технологий или уровня. Бывший техлид гугла рассказывает о них в этом видео.
Кого из них вы встречали? Расскажите в чате

Хорошего воскресенья.
👍1
День шестьсот седьмой. #ЗаметкиНаПолях
Паттерн Options. Начало
Паттерн Options помогает связывать конфигурацию приложения со строго типизированными классами. Преимущество этого подхода перед получением каждого свойства конфигурации по отдельности (помимо сокращения объёма кода) в том, что есть возможность группировать связанные свойства, а также производить валидацию свойств.
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}

public class PositionOptions {
public const string Position = "Position";

public string Title { get; set; }
public string Name { get; set; }
}
Заметьте, что используется статическая переменная Position с путём к секции конфигурации (в данном случае "Position"), чтобы избежать опечаток (см. ниже). Получить значение в коде можно через внедрение зависимости IConfiguration:
private readonly IConfiguration _configuration;
Далее либо через вызов метода ConfigurationBinder.Bind:
var positionOptions = new PositionOptions(); _configuration.GetSection(PositionOptions.Position)
.Bind(positionOptions);
либо через вызов метода ConfigurationBinder.Get<T>:
positionOptions =
_configuration.GetSection(PositionOptions.Position)
.Get<PositionOptions>();

Кроме того, можно добавить регистрацию параметров в сервисы:
public void ConfigureServices(IServiceCollection services) {
services.Configure<PositionOptions>(
Configuration.GetSection(
PositionOptions.Position));
// либо
services.AddOptions<PositionOptions>()
.Bind(Configuration.GetSection(
PositionOptions.Position));

}
И использовать через внедрение зависимости:
private readonly PositionOptions _options;
public TestModel(IOptions<PositionOptions> options) {
_options = options.Value;
}

Обновление значений
Что если нам нужно обновлять значения параметров во время работы приложения? IOptions<T> устанавливается как Singleton в контейнере DI. Значения параметров устанавливаются при первом обращении к ним, и не могут быть изменены без рестарта. Именованные параметры (о них далее) не поддерживаются.

В этом случае вместо IOptions<T> можно использовать один из двух вариантов:
1. IOptionsSnapshot<T>
Поддерживает перезагрузку параметров и именованные параметры. Регистрируется как Scoped сервис. Поэтому обновлённые значения параметров будут доступны при следующем запросе к приложению. Однако из-за регистрации как Scoped, IOptionsSnapshot<T> не может быть внедрён в Singleton сервисы.

2. IOptionsMonitor<T>
Поддерживает перезагрузку параметров и именованные параметры. Регистрируется как Singleton сервис. Значения параметров доступны через options.CurrentValue (в отличие от Value в предыдущих случаях). Кроме того, поддерживается метод OnChange, в котором регистрируется делегат, обновляющий значения в коде при изменении конфигурации, а также, например, делающий запись в лог.

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

Источник:
https://app.pluralsight.com/library/courses/dotnet-core-aspnet-core-configuration-options/
👍2
День шестьсот восьмой. #ЗаметкиНаПолях
Паттерн Options. Продолжение
Начало

Извлечение в интерфейс
Чтобы не использовать внедрения вроде IOptions<T> или IOptionsSnapshot<T> в классах приложения, можно выделить параметры в интерфейс
public class PositionOptions : IPositionOptions {
public const string Position = "Position";

public string Title { get; set; }
public string Name { get; set; }
}

public interface IPositionOptions {
string Title { get; }
string Name { get; }
}

Затем зарегистрируем интерфейс в сервисах, используя фабрику:
services.Configure<PositionOptions>(
Configuration.GetSection("Position"));
services.AddSingleton<IPositionOptions>(
sp => sp.GetRequiredService<
IOptions<PositionOptions>>().Value);

И используем готовый интерфейс:
public TestModel(IPositionOptions options) {
_options = options;
}

Именованные параметры
Именованные параметры полезны, когда несколько разделов конфигурации привязаны к одним и тем же свойствам. Например:
"ContactUs": {
"Manager": {
"Name": "Joe Smith",
"Phone": "555-01-01"
},
"CEO": {
"Name": "Jane Smith",
"Phone": "555-02-02"
}
}

public class ContactSettings {
public string Name { get; set; }
public string Phone { get; set; }
}

Тогда можно использовать перегруженную версию метода Configure, принимающую строковое имя:
services.
Configure<ContactSettings>("Manager",
Configuration.GetSection(
"ContactUs:Manager"));
services.Configure<ContactSettings>("CEO",
Configuration.GetSection("ContactUs:CEO"));

Получить значения параметров из сервиса можно, передав имя набора в метод Get:
private readonly ContactSettings _manager;
private readonly ContactSettings _ceo;

public TestNamed(
IOptionsSnapshot<ContactSettings> options) {
_manager = options.Get("Manager");
_ceo = options.Get("CEO");
}

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

Источник:
https://app.pluralsight.com/library/courses/dotnet-core-aspnet-core-configuration-options/
👍1
День шестьсот девятый. #ЗаметкиНаПолях
Паттерн Options. Окончание
Начало
Продолжение

Валидация
Валидацию параметров можно осуществлять несколькими способами:
1. Аннотации
Аналогично валидации модели, можно использовать атрибуты из System.ComponentModel.DataAnnotations:
public class PositionOptions {
public string Title { get; set; }
[Required]
public string Name { get; set; }
}

При этом в регистрации используется метод AddOptions, позволяющий добавить валидацию в цепочку:
services.AddOptions<PositionOptions>()
.Bind(Configuration
.GetSection("Position"))
.ValidateDataAnnotations();

2. Метод Validate
Можно реализовать более сложную, чем аннотации, логику валидации при регистрации:
services.AddOptions<PositionOptions>()
.Bind(Configuration
.GetSection("Position"))
.Validate(с => { … });
Метод должен возвращать результат валидации в виде булева значения.

3. IValidateOption<T>
При этом подходе создаётся класс валидации, реализующий IValidateOption<T>, например:
public class PositionOptionsValidation : 
IValidateOption<PositionOptions> {

}
Класс должен реализовать метод Validate интерфейса. Преимущество этого подхода в том, что для валидации можно использовать другие сервисы через DI. Регистрируется класс как Singleton после регистрации параметров:
services.Configure<MyConfigOptions>(
Configuration.GetSection("Position"));
services.TryAddEnumerable(
ServiceDescriptor
.Singleton<
IValidateOptions<PositionOptions>,
PositionOptionsValidation>());
Метод TryAddEnumerable позволяет регистрировать в DI коллекцию реализаций для интерфейса, поскольку у нас может быть несколько классов валидаторов.

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

В этом случае одним из вариантов может быть создание фонового сервиса, который будет запускаться вместе с приложением и проверять все параметры. Кроме того, ему можно передать реализацию IHostApplicationLifetime, позволяющую управлять основным приложением:
public class ValidateOptionsService : IHostedService { 
public ValidateOptionsService(
IHostApplicationLifetime appLifetime,
IOptions<PositionOptions> posOptions,
//другие параметры…) {
_appLifetime = appLifetime;
_posOptions = posOptions;

}
}

IHostedService предполагает реализацию двух методов, возвращающих Task: StartAsync и StopAsync. В последнем можно ничего не делать, просто вернуть Task.CompletedTask.
А в StartAsync добавляется код проверки параметров. Как вариант, при возникновении ошибок валидации можно останавливать приложение:
public Task StartAsync(CancellationToken ct) {
try {
//доступ к параметру приводит к валидации
_ = _posOptions.Value;
}
catch (OptionsValidationException ex) {
// останавливаем приложение
_appLifetime.StopApplication();
}

return Task.CompletedTask;
}

Источник: https://app.pluralsight.com/library/courses/dotnet-core-aspnet-core-configuration-options/
👍1
День шестьсот десятый. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
61. Завладейте Сборкой
Нередки случаи, когда команды разработчиков, которые в остальном очень дисциплинированно относятся к коду, пренебрегают дисциплиной относительно скриптов сборки. То ли думая, что это незначительная деталь, то ли из опасения, что релиз - это отдельный сложный процесс и вообще, забота другого отдела. В результате слабо сопровождаемые скрипты сборки с дублированием кода и ошибками вызывают проблемы не реже, чем плохо написанный код.

Одно из объяснений того, почему дисциплинированные и опытные разработчики рассматривают сборку как нечто второстепенное по сравнению с их работой, заключается в том, что скрипты сборки часто написаны на языке, отличном от языка исходного кода. Во-вторых, сборка - это «не совсем код». Эти оправдания противоречат тому факту, что большинству разработчиков программного обеспечения нравится изучать новые языки, и что сборка создаёт исполняемый код, которые тестируют разработчики и запускают конечные пользователи. Код бесполезен, пока он не будет собран. Сборка – вот что определяет компонентную архитектуру приложения. Она является важной частью процесса разработки, и правильная настройка сборки может упростить написание кода.

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

Традиционно тестирование всегда оставалось на усмотрение команды обеспечения качества (QA). Теперь мы понимаем, что тестирование в процессе написания кода необходимо для получения предсказуемых результатов. Точно так же процесс сборки должен принадлежать команде разработчиков.

Понимание сборки может упростить весь жизненный цикл разработки и снизить затраты. Простая в исполнении сборка позволяет новому разработчику быстро и легко приступить к работе. Автоматизация сборки может позволить вам получать согласованные результаты, когда над проектом работают несколько человек, избегая ситуаций вроде «а у меня оно работает». Многие инструменты сборки позволяют создавать отчеты о качестве кода, что позволяет заранее обнаруживать потенциальные проблемы. Потратив время на понимание того, как получить полный контроль над сборкой, вы можете помочь и себе, и всей вашей команде. После этого вы можете сосредоточиться на написании кода. Сборка больше не будет казаться этапом, на котором происходит неведомая магия, и ваша работа станет более приятной.

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

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Steve Berczuk
День шестьсот одиннадцатый. #ЧтоНовенького
Сканирование кода от GitHub
Сканирование кода - это функционал GitHub, позволяющий легко находить уязвимости безопасности до того, как они попадут в продакшн. Уже год GitHub сотрудничает с Semmle, чтобы предоставить пользователям возможности анализа кода по технологии CodeQL.

Сканирование кода предназначено в первую очередь для разработчиков. Вместо того, чтобы перегружать вас советами от анализатора кода, сканирование кода по умолчанию запускает только проверку актуальных правил безопасности. Оно интегрируется с GitHub Actions или другой системой CI/CD, чтобы обеспечить максимальную гибкость для вашей команды. Код сканируется по мере его создания, автоматизируя проверку безопасности. Это помогает гарантировать, что уязвимости никогда не попадут в продакшн.

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

Сканирование основано на открытом стандарте SARIF и является расширяемым, поэтому вы можете добавлять решения SAST (Статическое Тестирование Защищенности Приложений) как с открытым исходным кодом, так и коммерческие. Вы можете интегрировать сторонние механизмы сканирования, чтобы просматривать результаты всех проверок безопасности в едином интерфейсе, а также экспортировать результаты сканирования через единый API.

С момента представления бета-версии в мае просканировано более 12 000 репозиториев и обнаружено более 20 000 уязвимостей вроде удаленного выполнения кода (RCE), SQL-инъекций и межсайтовых сценариев (XSS).

Сканирование кода бесплатно для публичных репозиториев. Для закрытых репозиториев сканирование кода доступно в GitHub Enterprise через Advanced Security.

Источник: https://github.blog/2020-09-30-code-scanning-is-now-available/
День шестьсот двенадцатый. #Оффтоп
Топ 10 Советов по Работе в VS Code
Visual Studio Code уже несколько лет держит первое место по популярности среди сред разработки, согласно аналитики на StackOverflow. Сегодня представлю несколько советов по эффективной работе в этом редакторе.

1. Сочетания клавиш
Самый быстрый способ навигации по окнам и файлам проекта – это запомнить несколько сочетаний клавиш. В этом вам поможет страница сочетаний клавиш, которую можно открыть по Ctrl+K,S.
Наверное, наиболее важное сочетание – вызов панели команд (Command Palette) с помощью Ctrl+P. Откроется строка универсального поиска, позволяющая искать файлы, команды или пункты меню. Чтобы увидеть все возможности, введите ?.

2. Расширения
Сам по себе редактор VS Code достаточно лёгкий. Вся его мощь в огромном количестве доступных расширений. Более того, при открытии проекта редактор сам предложит вам установить рекомендуемые для конкретного языка расширения. Например, мне при открытии проекта на C# предложил установить расширение "C# for Visual Studio Code".
Ещё одно крутое расширение "Paste JSON as Code" позволяет вставлять код JSON отформатированный в класс на соответствующем языке (поддерживается множество языков). Например, в файл .cs вставится класс на C#, а в файл .ts – класс на TypeScript.

3. Сниппеты
Доступны через расширения (например, "C# Snippets"). Позволяют существенно сократить затраты на написание шаблонных блоков. И не ограничиваются обычными prop или ctor, а позволяют вставлять целые классы, например, ASP.NET MVC контроллера.

4. Окно Терминала
Окно терминала, содержащее Windows Power Shell или аналогичный терминал в других ОС, можно вызвать по Ctrl+` (Ctrl+ё в русской раскладке).

5. IntelliSense
Всем знакомый инструмент автодополнения кода. Работает в строго типизированных языках либо автоматически, либо по нажатию Ctrl+пробел.

6. Просмотр определения
Знакомый всем по Visual Studio инструмент «Перейти к определению» F12 или «Посмотреть определение» Ctrl+F12. И наоборот, в определении класса или интерфейса вы можете найти все ссылки на него по Shift+F12. А также переименовать все упоминания по F2 с возможностью предпросмотра изменений. Кроме того, введя @ в панели команд, вы можете посмотреть все классы, методы, свойства и т.п. в текущем файле и быстро к ним перейти. Доступно через упомянутое выше расширение (или аналогичное в другом языке).

7. Zen Mode
Чтобы убрать всё, что вас отвлекает от работы, можно перейти в Zen Mode (Ctrl+K,Z). Редактор перейдёт в полноэкранный режим, уберёт все меню, боковые и нижние панели. Ничего лишнего, только код.
Для полного расслабона есть даже расширение для Spotify, но оно работает только с премиум подпиской.

8. Отладка
Для этого также придётся установить расширение под соответствующий язык. Ничего сложного, просто откройте панель команд (Ctrl+P), наберите > для команд и "Debugger". Подсказка предложит установить отладчик для текущего языка. Для отладки клиентский код прямо в VS Code можно установить, например, "Debugger for Chrome".

9. Git
Работа с Git уже встроена в VS Code. Просто перейдите на вкладку Source Control слева или нажмите Ctrl+Shift+G. Вам откроется список изменённых файлов. Всё, что остаётся сделать, это ввести сообщение и нажать Commit. Другие возможности Git доступны в меню по кнопке "…".
А если установить расширение GitLens, вы сможете не только работать с репозиторием в панели Source Control, но и видеть в коде подсказки, кто и когда изменил определённые блоки кода.

10. Visual Studio Live Share
Расширение для командной разработки в режиме реального времени. Позволяет совместно вносить изменения, совместно выполнять отладку, созваниваться и общаться в чате с коллегами, просматривать комментарии, предоставлять общий доступ к терминалам и серверам, и т.д., и т.п.

Источник: https://youtu.be/u21W_tfPVrY
День шестьсот тринадцатый. #Оффтоп #ВопросыНаСобеседовании
Поведенческое интервью
У меня на канале было несколько постов на тему «Что спрашивают на собеседовании?», но до сих пор я не затрагивал эту тему. Если не понятно из названия, на этом собеседовании задают вопросы о вас лично, пытаясь узнать, какой вы человек, ваш опыт, ваши сильные и слабые стороны, пытаясь определить, какой из вас выйдет работник. Обычно это один из первых этапов собеседования (или первое в серии). Несмотря на то, что здесь, казалось бы, не проверяют ваши знания, некоторые вопросы могут ввести в ступор, либо ваши ответы могут быть неверно восприняты настолько, что вашу кандидатуру отметут уже на начальном этапе.

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

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

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

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

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

Одно из замечаний от интервьюера в видео мне также показалось интересным:
Не используйте абсолютные выражения.
В видео «кандидат» заявил, что никогда бы не стал работать с легаси-кодом. Это очень сильное заявление сразу в нескольких аспектах. Во-первых, легаси код есть в каждой компании (кроме, может быть, только что созданных стартапов). Может быть в компании много легаси кода, который они хотят или не хотят переписывать (если работает, не трогай). Во-вторых, код – такая вещь, что самая свежая и прорывная технология через пару лет станет легаси, а то и вовсе умрёт (R.I.P Silverlight). Но и в остальных вопросах избегайте использования категоричных суждений. Это как раз тот момент, когда за нечаянно вырвавшееся у вас «никогда» могут зацепиться и истолковать вашу мысль совсем не так, как вы пытались её донести.

Что вы думаете? Какие самые сложные, неприятные или глупые нетехнические вопросы вам задавали на собеседовании? Телеграм на днях выкатил обновление, позволяющее добавлять комментарии (надеюсь, клиенты уже у всех обновились). Давайте их опробуем!
Источник: https://www.youtube.com/watch?v=GxCD7Q_qDpU
👍1
День шестьсот четырнадцатый. #ЗаметкиНаПолях
Внедрение Зависимостей в
ASP.NET Core. Начало (1/4)
ASP.NET Core активно использует внедрение зависимостей. Теорию я описывал в этом посте, а тут будет только практика.
По умолчанию в приложениях используется контейнер внедрения зависимостей (контейнер DI) от Microsoft, который несколько ограничен в функционале, по сравнению с другими, но подходит в большинстве случаев. Он находится в отдельной библиотеке в пространстве имён Microsoft.Extensions.DependencyInjection. В проект он обычно включается в составе метапакета Microsoft.AspNetCore.All.

Необходимые для внедрения интерфейсы и классы (также называемые сервисами) регистрируются в коллекции типа IServiceCollection. Чаще всего это происходит в методе ConfigureServices класса Startup. Во время работы приложения контейнер DI отвечает за обнаружение необходимого сервиса и создание его экземпляра с нужным сроком жизни.
public void ConfigureServices(IServiceCollection services) { … }
Параметр services на момент вызова метода будет инициализирован хостом. В отличие от регистрации промежуточного ПО в методе Configure, порядок регистрации сервисов чаще всего не имеет значения.

Что регистрировать в сервисах?
В первую очередь – зависимости. Просмотрите свой код и найдите использования ключевого слова new. Если ваш класс A создаёт экземпляр класса B, вызывает его методы и использует результат их работы, то класс A зависит от класса B.
class A {
public MethodA() {
var b = new B();
b.MethodB();
}
}
Класс B - кандидат на регистрацию в сервисах. В общем случае это делается в 3 этапа:
1. Выделяем интерфейс из класса B:
interface InterfaceB {
void MethodB();
}
class B : InterfaceB {…}

2. Добавляем интерфейс в параметры конструктора класса A:
class A {
private readonly InterfaceB _b;

public A(InterfaceB b) {
_b = b;
}
}

3. Регистрируем реализацию интерфейса в контейнере DI:
services.AddTransient<InterfaceB, B>();

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

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

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

Источник:
https://app.pluralsight.com/library/courses/aspdotnet-core-dependency-injection/
День шестьсот пятнадцатый. #ЗаметкиНаПолях
Внедрение Зависимостей в
ASP.NET Core. Продолжение (2/4)
Начало

Время жизни сервисов
Контейнер DI отвечает за создание и уничтожение экземпляров сервисов. Доступно 3 варианта регистрации:
1. Transient – новый экземпляр сервиса создаётся каждый раз, когда он требуется. Каждый класс, зависящий от такого сервиса, получит отдельный экземпляр:
services.AddTransient<IEmailSender, EmailSender>();
- самый простой в работе: если срок жизни сервиса сложно определить, Transient сервис чаще всего будет наиболее подходящим;
- не требует потокобезопасности, т.к. не подходит для сохранения состояния;
- потенциально может неэффективно использовать память и вести к созданию и уничтожению множества объектов.

2. Singleton – экземпляр создаётся на всё время работы приложения. Один и тот же экземпляр сервиса будет использоваться во всех зависящих от него классах всё время:
services.AddSingleton<ILoggerFactory, LoggerFactory>();
- изменение состояния сервиса (если оно допускается) должно быть потокобезопасно;
- обычно более эффективен, чем Transient, т.к. создаётся только один объект, поэтому подходит для дорогих в создании объектов,
- может приводить к утечкам памяти при регистрации больших редко используемых объектов;
- подходит для объектов с общими функциями, не сохраняющими состояние (например, логгер).

3. Scoped – экземпляр сервиса создаётся на время обработки запроса к приложению. Зависимые объекты в течение обработки одного запроса получат один и тот же экземпляр. Например, в EntityFramework класс DbContext регистрируется как Scoped:
services.AddScoped<IOrderService, OrderService>();

Избегайте захвата сервисов
Сервис не должен зависеть от другого сервиса с меньшим временем жизни! Если Singleton-сервис зависит от Transient- или Scoped-сервиса, то последний может быть захвачен и не будет уничтожен в течение всего времени жизни приложения. Это может приводить как к утечкам памяти, так и к совместному использованию непотокобезопасного кода из Transient-сервисов.
Начиная с ASP.NET Core 2.0 по умолчанию включена валидация времени жизни сервисов (ValidateScopes). Она приводит к ошибке времени выполнения при старте приложения, если будет обнаружен захват сервисов. Однако, учтите, что эта функция отключена в продакшн, и её не рекомендуется включать вне среды разработки.

Регистрация открытых обобщённых типов
Иногда требуется зарегистрировать реализацию обобщённого интерфейса. Например, ILogger<T>. В этом случае используется необобщённый метод Add… и typeof:
services.AddSingleton(
typeof(ILogger<>), typeof(MyLogger<>));
Если параметров типа больше одного, внутрь угловых скобок добавляются запятые(например, typeof(ISomeInterface<,>)).

Дескрипторы сервисов
Дескрипторы сервисов (класс ServiceDescriptor) содержат информацию о регистрируемых сервисах и используются внутри контейнера DI.
var sd = new ServiceDescriptor(typeof(IOrderService),
typeof(OrderService), ServiceLifetime.Scoped);
services.Add(sd);
Чаще всего создание дескриптора скрыто в методах расширения, описанных выше, и создание его вручную не требуется.

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

Источник:
https://app.pluralsight.com/library/courses/aspdotnet-core-dependency-injection/
День шестьсот шестнадцатый. #ЗаметкиНаПолях
Внедрение Зависимостей в
ASP.NET Core. Продолжение (3/4)
Начало
Продолжение

Регистрация нескольких сервисов
Если в методе ConfigureServices используется несколько регистраций для одного интерфейса, то последняя из них имеет приоритет.
services.AddTransient<IMessageSender, EmailSender>();
services.AddTransient<IMessageSender, SMSSender>();
Здесь в приложении будет использован SMSSender.

Чтобы избежать случайной «перезаписи» зарегистрированных сервисов, можно использовать соответствующий TryAdd… метод (например, TryAddTransient). Тогда реализация будет добавлена, только если она ещё не была зарегистрирована ранее.
Важно отметить, что без использования TryAdd… зарегистрированы будут оба сервиса, но лишь последняя реализация будет использована при обнаружении сервиса.

Иногда нужно явно перезаписать предыдущую регистрацию. Тогда используется метод Replace, принимающий дескриптор сервиса:
services.Replace(
new ServiceDescriptor(typeof(IMessageSender),
typeof(SMSSender), ServiceLifetime.Transient)
);
Чтобы удалить все регистрации сервисов для определённого интерфейса, используется метод RemoveAll:
services.RemoveAll<IMessageSender>();

Использовать эти методы придётся редко. Например, чтобы предоставить свою реализацию одного из сервисов сторонней библиотеки.

Зачем же контейнер DI регистрирует несколько реализаций? Это может пригодиться, когда вам нужно, например, зарегистрировать несколько отправителей сообщений, валидаторов данных или бизнес-правил, которые должны будут применены одно за другим. В этом случае в конструктор зависимого класса внедряется коллекция сервисов:
public class Notifier {
IEnumerable<IMessageSender> _senders;

public Notifier(
IEnumerable<IMessageSender> senders) {
_senders = senders;
}
public void SendMessage() {
foreach (var s in _senders) {
// обработка каждого отправителя
}
}
}

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

При регистрации нескольких реализаций может быть нежелательно регистрировать одинаковые реализации, что обычными методами не проверяется. В нашем случае, если случайно зарегистрировать SMSSender дважды, сообщение по смс будет отправлено 2 раза. Тогда можно использовать метод TryAddEnumerable, принимающий коллекцию дескрипторов сервисов:
services.TryAddEnumerable(new[]
{
new ServiceDescriptor(typeof(IMessageSender),
typeof(EmailSender), ServiceLifetime.Transient),
new ServiceDescriptor(typeof(IMessageSender),
typeof(SMSSender), ServiceLifetime.Transient),
new ServiceDescriptor(typeof(IMessageSender),
typeof(SMSSender), ServiceLifetime.Transient)
});
В этом случае будет зарегистрирована только одна реализация SMSSender.

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

Источник:
https://app.pluralsight.com/library/courses/aspdotnet-core-dependency-injection/