.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
День 1496. #ProjectManagement
Хватит Говорить: «Технический Долг». Начало
Представления людей о «техническом долге» немного отличаются.

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

Звучит знакомо? Это разочаровывающий разговор.

Но мы часто сами приводим себя в эту ситуацию. Мы пытаемся объединить бизнесменов, дизайнеров, продакт-менеджеров и инженеров, используя фразу «технический долг». Но для каждого эта фраза означает своё. Спросите 10 технических специалистов, что такое технический долг, скорее всего получите 5-7 разных ответов.

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

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

Мы, инженеры, должны скорректировать терминологию.

Приравнивание технического долга к просто плохому коду имеет несколько проблем.

1. Позволяет думать, что предыдущие разработчики просто халтурили, что нетактично, но нормально, пока мы не поймем, что на самом деле существовало ограничение, о котором мы не знали. Это ограничение объясняет отвратительные характеристики кода, а также мешает нам реализовать наше гениальное решение.

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

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

3. Позволяет нам путать «этот код не соответствует моим личным предпочтениям» с «этот код является проблемой» — что, опять же, нормально, пока мы не ограничены во времени. Мы тратим «неделю технического долга», занимаясь рефакторингами, вместо того чтобы что-то исправлять. Инженеры любят рефакторить код, чтобы он выглядел лучше. В итоге работать с кодом не становится легче, чем раньше: код просто другой, и никто, кроме автора, больше его не знает. Это одна из основных причин того, что недели «выплаты технического долга» часто мало или совсем ничего не прибавляют к скорости команды после них.

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

Источник:
https://stackoverflow.blog/2023/02/27/stop-saying-technical-debt/
👍12
День 1497. #ProjectManagement
Хватит Говорить: «Технический Долг». Окончание
Начало

Чтобы решить эти проблемы, выберите что-то измеримое для оценки качества системы, например: нагрузка по обслуживанию. Сколько времени и усилий разработчики тратят на задачи, не связанные с добавлением или удалением функций? Если у нас 6 разработчиков, но половина работы приходится на обслуживание, то надо планировать выпуск функций, полагаясь только на 3 разработчиков. Мы также можем отслеживать это число и определять, насколько быстро оно растёт с течением времени. Чем быстрее растёт нагрузка по обслуживанию, тем больше разочарований мы можем ожидать. Нулевой рост означает, что мы всегда можем обслуживать систему с той же долей нашей команды инженеров.

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

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

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

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

Мы работаем с устаревшей унаследованной архитектурой, основанной на предположениях о предметной области, которые больше не соответствуют действительности? Может быть, нужно создать план перестройки архитектуры.

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

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

Источник: https://stackoverflow.blog/2023/02/27/stop-saying-technical-debt/
👍6
День 1498. #ЧтоНовенького
Blazor United Включён в Дорожную Карту .NET 8
В конце января создатель Blazor Стив Сандерсон представил Blazor United https://youtu.be/48G_CEGXZZM. Проект направлен на объединение разрозненных компонентов экосистемы ASP.NET Core для формирования новой инструментальной среды, заимствуя технологии у Razor Pages, MVC, CSHTML, Blazor Server и Blazor WebAssembly.

«Мы начали несколько экспериментов, чтобы объединить преимущества Razor Pages, Blazor Server и Blazor WebAssembly в одном продукте, что позволит компонентам Blazor стать единой архитектурой для всех ваших сценариев веб-UI: для простого рендеринга HTML или для полной интерактивности либо на стороне сервера, либо в WebAssembly — и всё это в одном проекте с возможностью легко переключаться между различными режимами рендеринга и даже смешивать их на одной странице».

В видео он попросил оставлять отзывы о том, кажется ли Blazor United актуальным для разработчиков и каковы будут их сценарии и приоритеты. Он также сказал, что команда серьезно настроена сделать что-то вроде Blazor United для .NET 8, который выйдет в ноябре.

В целом полученная обратная связь была хорошей.

Вот что другой разработчик и автор Blazor Крис Сэйнти думает о проекте:
«Я в восторге от Blazor United по нескольким причинам, — сказал Сэйнти журналу Visual Studio Magazine. — Во-первых, похоже, что это позволит нам преодолеть некоторые из основных компромиссов при использовании Blazor WebAssembly или Blazor Server. С Blazor Wasm главной проблемой всегда было время, чтобы загрузить приложение и запустить его – ведь нужно скачать всю среду исполнения .NET. С Blazor Server проблема заключалась в необходимости постоянного подключения через SignalR и возникающих проблемах масштабирования. Эта возможность смешивать модели хостинга была тем, о чём люди просили с самых первых дней существования Blazor».

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

«Вторая вещь, которая меня волнует, — это возможность просто отображать страницы на традиционном сервере с использованием модели программирования Blazor. Мне кажется, это сделает MVC и Razor Pages устаревшими, поскольку разработчики смогут использовать Blazor для любого типа веб-приложений в будущем. Я знаю, что не всем это понравится, но я бы хотел, чтобы Microsoft отказалась от MVC и Razor Pages и двигалась вперед с Blazor в качестве единственной модели для создания веб-интерфейса на основе стека Microsoft. Это значительно упростит вход в экосистему. Когда также будет добавлен Blazor Hybrid, разработчики могут потратить время на изучение одной модели программирования и создавать веб-UI и UI для настольных компьютеров и мобильных устройств в одной среде».

За ходом проекта можно следить в ветке Blazor United репозитория ASP.NET Core на GitHub.

Источник: https://visualstudiomagazine.com/articles/2023/02/03/blazor-united-plan.aspx?m=1
👍10
День 1499. #ЗаметкиНаПолях
Как Использовать yield break в C#?
Когда мы помещаем оператор yield внутрь метода, компилятор обрабатывает этот метод как метод (блок) итератора. Метод итератора может возвращать IEnumerable, IEnumerator, IEnumerable<T> или IEnumerator<T>. Он использует оператор yield return для возврата элементов один за другим. Компилятор обрабатывает метод итератора не так, как обычный метод. Про yield return я уже писал ранее, сегодня рассмотрим yield break.

Как только мы определим метод итератора с помощью yield return, нам понадобится способ остановить итерацию в какой-то момент. Например, когда выполняется какое-то условие, или после выполнения заданного количества итераций и т. д. Для этой цели мы можем использовать оператор yield break. Он завершит итерацию и выйдет из блока итератора. Рассмотрим метод, который будет генерировать случайные года между 1901 и 2023. Он будет выполняться бесконечно, поэтому прервём его, если будет найден високосный год:
public static class Utils
{
private static Random random = new();
public static IEnumerable<int> RandomYears()
{
int year;
while (true)
{
year = random.Next(1901, 2023);
yield return year;

if (year % 4 == 0)
{
Console.WriteLine($"Leap Year:{year}");
yield break;
}
}
Console.WriteLine($"Finished");
}
}

Теперь посмотрим на результаты:
foreach (int year in Utils.RandomYears())
Console.WriteLine(year);

Вывод:
1937
1986
1942
1920
Leap Year:1920

Может возникнуть вопрос: можем ли мы просто использовать break для завершения итерации вместо yield break? Да, но они ведут себя по-разному:
- yield break внутри блока итератора полностью завершает блок итератора и выходит из метода. Даже если после цикла есть какие-то операторы, компилятор их не выполнит. Как это видно в результатах выше, нет строки “Finished”. В этом случае компилятор даже выдаст предупреждение о том, что он обнаружил недостижимый код.
- break внутри цикла, он завершает цикл и выполняет следующий оператор после цикла. Т.е., если есть другие операторы после цикла, компилятор все равно выполнит их перед выходом из метода.

Если в коде выше заменить yield break на break и выполнить код, мы увидим выполнение оператора после цикла:
1969
1995
1920
Leap Year:1920
Finished

Мы можем думать об операторе yield break внутри метода итератора как о чём-то похожем на оператор return внутри обычного метода, который ничего не возвращает. Но можем ли мы просто использовать return для выхода из итератора вместо использования yield break? Нет. Мы увидим ошибку компиляции: CS1622 - Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. (Невозможно вернуть значение из итератора. Используйте оператор yield return, чтобы вернуть значение, или yield break, чтобы завершить итерацию.)

Итого
Методы итератора отличаются от обычных методов и не поддерживают обычные операторы возврата. Он может использовать только операторы yield return и yield break. Это очень важное различие между методами итератора и обычными методами. Метод итератора может иметь операторы «yield return» для возврата значений одно за другим и операторы yield break для выхода из итератора. Но мы не можем использовать обычный оператор return в методе итератора.

Источник: https://code-maze.com/csharp-yield-break/
👍16
День 1500. #ЗаметкиНаПолях
Избавляемся от Предупреждений Nullable-Типов в Объектах JSON
Использование обнуляемых ссылочных типов поможет вам избежать неприятных ошибок, а IDE сообщит вам о местах потенциальных проблем. Но как их использовать в типах, которые десериализуются из JSON?

Проблема в следующем: в проекте обычно есть несколько объектов передачи данных (DTO) или обычных объектов (POCO), которые объявляют свойства для десериализации данных. Вы точно знаете, что данные будут там после десериализации, поэтому объявляете свойства необнуляемыми. Тем не менее, компилятор (и IDE) настаивают на том, чтобы вы либо сделали свойство обнуляемым, либо инициализировали его. Есть несколько вариантов исправить это, каждый со своими преимуществами и нюансами.

1. Сделать свойство обнуляемым
public class User
{
[JsonProperty("name")]
public string? Name { get; set; }
}
Это уберёт предупреждение, но теперь придётся везде проверять свойство Name на null. Если вы точно знаете, что свойство всегда будет иметь значение, это добавляет много накладных расходов в вашу кодовую базу.

2. Добавить значение default! (пожалуйста, не надо)
Вы можете инициализировать свойство значением default!. Это фактически устанавливает значение в null, но подавляет предупреждение.
public string Name { get; set; } = default!;

Не делайте этого. Если десериализованный JSON не содержит значения свойства Name, теперь оно будет содержать null. Компилятор и IDE не будут предупреждать вас об этом, т.е. во время выполнения может быть выброшено неожиданное NullReferenceException.
Смысл обнуляемых ссылочных типов в том, чтобы предоставить вам систему безопасности, а это саботирует её. Подробнее про обнуляемый null можно почитать здесь.

3. Добавить первичный конструктор
Если вы используете Newtonsoft.Json, вы можете добавить в свой класс первичный конструктор, который устанавливает свойства. Десериализатор обнаружит это и вызовет конструктор вместо прямой установки свойств:
public class User
{
public User(string? name)
{
Name = name ?? "Unknown";
//или ArgumentNullException.ThrowIfNull(name)
}

[JsonProperty("name")]
public string Name { get; init; }
}
Предупреждение исчезнет, и ваш класс близок к JSON, который хотите десериализовать. Если вы уверены, что в JSON не будет null, имеет смысл использовать свойство, не допускающее null, и в C#.
Можно либо установить значение по умолчанию, либо выбрасывать ArgumentNullException в конструкторе. Последний вариант может означать исключение во время выполнения, но оно возникнет только тогда, когда что данные JSON не соответствуют вашим ожиданиям, и могут потребоваться другие действия (например, запись инцидента в лог) вместо того, чтобы просто продолжать выполнение кода.

4. Использовать required свойство
В C# 11 можно использовать модификатор required. В этом случае компилятор ожидает, что свойство всегда будет инициализировано и предупреждения не будет:
public required string Name { get; set; }

Такой подход чётко формулирует ожидания, не предоставляя компилятору и IDE ложной информации.
Имейте в виду, что модификатор required применяется во время компиляции, а не во время выполнения. Если JSON содержит null, нет никакой гарантии, что вы избежите NullReferenceException. В этом случае лучше использовать string? и выполнять проверки на null, где это применимо.

Источник: https://blog.maartenballiauw.be/post/2023/01/12/getting-rid-of-warnings-with-nullable-reference-types-and-json-object-models-in-csharp.html
👍8
День 1501. #Карьера
Почему Программирование Похоже на Спорт
Думайте о программировании как о спорте. Вам не нужно любить спорт или преуспевать в нём. Тем не менее, это хорошая аналогия.

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

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

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

Любой программист посоветует вам приложить много усилий для практики кодирования. Так же, как если вы хотите добиться успеха в теннисе, вы должны много тренироваться играть. То же самое с написанием кода. Практика, практика, практика! И хотя это отличный совет, можно продолжить аналогию и сосредоточимся на идее «набора навыков».

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

Чтобы добиться успеха в баскетболе, вы должны хорошо выполнять эти отдельные упражнения. Играя в настоящую игру, вы должны быстро переключаться между этими навыками. Время, потраченное на отработку каждого навыка, поможет делать это автоматически. Поэтому вместо того, чтобы думать: «Как мне сделать пас?» вы можете сосредоточиться на игре, происходящей вокруг вас, чтобы вопрос был «надо ли делать пас?». При достаточной практике вы можете вообще не думать; вы будете действовать по наитию!

Независимо от того, занимаетесь ли вы спортом или учитесь программировать, практика «набора навыков» сделает вас лучше. Невозможно улучшать все индивидуальные навыки и не совершенствоваться в целом. И наоборот, если вы не видите прогресса в целом, то в вашем наборе отсутствует важный навык, потому что вы недостаточно его практикуете.

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

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

Итого
Поскольку программирование по своей сути - навык, это объясняет такое понятие как ад учебных пособий (Tutorial Hell). Просмотр или чтение учебных пособий может быть полезным, но ничто не заменит отработку ваших навыков в проекте. Просмотр видео на Youtube или Udemy не заведёт вас далеко, потому что это больше похоже на просмотр тенниса, чем на игру в него. Вы наверняка получите какие-то концептуальные знания, но не разовьёте навыки.

PS: вот примерный список навыков для .NET разработчика на 2023й года от Ника Чапсаса https://raw.githubusercontent.com/Elfocrash/.NET-Backend-Developer-Roadmap/master/roadmap-dark-compact-2023.png

Источник: https://betterprogramming.pub/why-coding-is-like-playing-sports-848fbb21282
👍23👎1
День 1502. #DesignPatterns
Паттерн Скромный Объект
Скромный Объект (Humble Object) — это паттерн проектирования, специально предназначенный для упрощения модульного тестирования путём отделения поведения, которое легко обработать (логика предметной области), от поведения, которое трудно обработать (например, внешние события или зависимости). Рассмотрим, что это такое и как его можно использовать.

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

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

Простыми словами: нужно выделить сложные для тестирования части и поместить их в оболочку, вместо которой в тестах вы можете использовать заглушку или мок. Эта оболочка называется Humble object.

У вас когда-нибудь была зависимость от DateTime.Now? Если да, может быть очень сложно иметь стабильные тесты в зависимости от того, что вы делаете с Now. Тесты могут работать в будние дни, но проваливаться в выходные, если вы проверяете это в коде. Поэтому мы можем просто «извлечь» эту часть и поместить её в оболочку:
public interface IDateTimeProvider
{
DateTime Now { get; }
}

public class DateTimeProvider : IDateTimeProvider
{
public DateTime Now => DateTime.Now;
}

public class MyService
{
public MyService(
IDateTimeProvider dateTimeProvider)
{

}
}

Мы отделили трудно тестируемые вещи от легко тестируемых. Трудно тестируемые вещи, в идеале, вообще не нуждаются в тестировании. Тестировать DateTime.Now вообще не лучшая идея. Во-первых, как бы вы реализовали такой тест? А во-вторых, это не ваш код. С вашей точки зрения, DateTime.Now — это сторонний код, и не ваша задача его тестировать, просто допустим, что он работает правильно.
В то же время легко тестируемые вещи, как MyService, теперь получают зависимость, которая находится под вашим контролем. В тестах её можно заменить пустышкой или моком.

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

Источник: https://steven-giesel.com/blogPost/47acad0a-255c-489b-a805-d0f46bde23e5
👍16👎1
День 1503. #ЗаметкиНаПолях
Использование Нескольких Контекстов EF Core. Начало
EF Core использует DbContext, который представляет сеанс с БД и отвечает за отслеживание изменений, выполнение операций и управление подключениями. Обычно в приложении используется только один DbContext. Но что, если нужно иметь несколько?

Зачем?
1. Несколько баз данных. Вы вынуждены использовать несколько контекстов, каждый из которых предназначен для своей БД.
2. Разделение проблем. Если модель предметной области сложная, можно упростить себе жизнь, разделив задачи между несколькими контекстами, каждый из которых отвечает за определённую часть.
3. Модульный монолит. Вы можете настроить свою схему базы данных для каждого контекста, обеспечивая логическое разделение на уровне базы данных.
4. Экземпляр для чтения. Вы можете настроить отдельный экземпляр DbContext только для чтения из БД, например, установив QueryTrackingBehavior.NoTracking на уровне DbContext, чтобы повысить производительность запросов.

Рассмотрим пример с двумя контекстами:
- оба используют одну и ту же БД,
- каждый обращается к отдельной схеме.
public class CatalogDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
public class OrderDbContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<LineItem> LineItems { get; set; }
}

Сначала нам нужно добавить контексты в контейнер DI:
services.AddDbContext<CatalogDbContext>(opt =>
opt.UseSqlServer("CONNECTION_STRING"));
services.AddDbContext<OrderDbContext>(opt =>
opt.UseSqlServer("CONNECTION_STRING"));

Если вы просто хотите использовать оба контекста в одной схеме, то это всё. Если вы хотите настроить разные схемы для каждого, необходимо переопределить метод OnModelCreating и указать схему с помощью HasDefaultSchema:
public class CatalogDbContext : DbContext
{

protected override void
OnModelCreating(ModelBuilder mb)
{
mb.HasDefaultSchema("catalog");
}
}

public class OrderDbContext : DbContext
{

protected override void
OnModelCreating(ModelBuilder mb)
{
mb.HasDefaultSchema("order");
}
}

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

Источник:
https://www.milanjovanovic.tech/blog/using-multiple-ef-core-dbcontext-in-single-application
👍5
День 1504. #ЗаметкиНаПолях
Использование Нескольких Контекстов EF Core. Окончание
Начало

Ограничения
- Невозможно выполнять соединения между разными экземплярами DbContext, поскольку EF Core не знает, используют ли они одну и ту же базу данных.
- Транзакции будут работать, только если DbContexts используют одну и ту же базу данных. Вы должны создать новую транзакцию и поделиться ею между контекстами, вызвав метод UseTransaction().

Таблица истории миграций
Если вы решите использовать разные схемы для каждого DbContext, вы можете быть неприятно удивлены, узнав, что схема по умолчанию не применяется к таблице истории миграций. Нужно настроить это, вызвав метод MigrationsHistoryTable и указав имя таблицы и схему, в которой будет храниться история миграций для этого контекста:
services.AddDbContext<CatalogDbContext>(opt =>
opt.UseSqlServer(
"CONNECTION_STRING",
o => o.MigrationsHistoryTable(
tableName: "efmigrations",
schema: "catalog")));

services.AddDbContext<OrderDbContext>(opt =>
opt.UseSqlServer(
"CONNECTION_STRING",
o => o.MigrationsHistoryTable(
tableName: "efmigrations",
schema: "order")));

Преимущества использования нескольких контекстов
Использование нескольких DbContexts может дать вашему приложению несколько преимуществ:
- Разделение ответственности
Каждый DbContext может отвечать за определённое подмножество данных приложения, что может помочь организовать код и сделать его более модульным.

- Повышение производительности
Когда вы разделяете доступ к данным на несколько DbContext, приложение может снизить риск конфликтов и улучшить параллелизм, что может повысить производительность.

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

Источник: https://www.milanjovanovic.tech/blog/using-multiple-ef-core-dbcontext-in-single-application
👍9
День 1505. #BestPractices
20 Вещей, Которым я Научился за 20 Лет. Начало
Учиться у тех, кто был до нас, очень важно для успеха, но мы часто забываем одну важную оговорку. Почти все советы зависят от контекста, но они редко полезны во всех случаях. Без понимания контекста совет не имеет смысла или, что ещё хуже, вреден.

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

1. Я всё ещё очень многого не знаю
«Как можно не знать, что такое Kafka?», «Ты никогда не слышал о Rust?» Большинство из нас слышали такого рода заявления. Многие из нас любят разработку ПО, потому что мы учимся на протяжении всей жизни, и в ПО, независимо от того, в каком направлении вы развиваетесь, есть огромные области знаний, которые развиваются во всех направлениях и расширяются с каждым днём. Вы можете потратить десятилетия карьеры и всё ещё иметь огромный пробел в знаниях по сравнению с кем-то, кто также провел десятилетия в, казалось бы, похожей роли. Чем раньше вы это поймёте, тем скорее сможете избавиться от синдрома самозванца и вместо этого получать удовольствие от обучения и обучения других.

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

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

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

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

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

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

Источник:
https://www.simplethread.com/20-things-ive-learned-in-my-20-years-as-a-software-engineer/
👍27
День 1506. #BestPractices
20 Вещей, Которым я Научился за 20 Лет. Продолжение
Начало

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

8. Каждая система в итоге становится плохой, смиритесь
У Бьерна Страуструпа есть цитата: «Есть только два вида языков: те, на которые люди жалуются, и те, которые никто не использует». Это может быть распространено и на большие системы. Не существует «правильной» архитектуры, вы никогда не спроектируете идеальный интерфейс, тесты всегда будут медленными. Это не повод не улучшать ситуацию, но меньше беспокойтесь об элегантности и совершенстве. Стремитесь к постоянному улучшению и созданию пригодной для жизни системы, в которой вашей команде будет приятно работать и которая будет стабильно приносить пользу.

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

10. Лучше избегать программистов 0,1x, чем искать программистов 10x
10-х программист — глупый миф. Идея, что кто-то может сделать за день то, что другой компетентный, трудолюбивый, такой же опытный программист может сделать за 2 недели, глупа. Некоторые пишут в 10 раз больше кода, а затем его приходится исправлять в 10 раз чаще. Кто-то может стать 10x программистом только при сравнении с 0,1x программистом, который тратит время впустую, не спрашивает отзывов, не тестирует свой код, не рассматривает пограничные случаи и т. д. Лучше не допустить в команду программистов 0,1x, чем найти мифического программиста 10x.

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

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

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

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

Источник:
https://www.simplethread.com/20-things-ive-learned-in-my-20-years-as-a-software-engineer/
Автор оригинала: Justin Etheredge
👍14
День 1507. #BestPractices
20 Вещей, Которым я Научился за 20 Лет. Окончание
Начало
Продолжение

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

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

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

17. Держите свои процессы как можно более экономичными
В наши дни все хотят быть гибкими, но быть «гибким» означает строить вещи небольшими порциями, учиться, а затем повторять. Если кто-то пытается втиснуть в этот процесс гораздо больше, чем нужно, то он, вероятно, что-то продаёт. Это не значит, что людям не нужно доверять и не полагаться на них в таких случаях. Но сколько раз вы слышали, как кто-то из крупной компании или крупного проекта хвастался тем, насколько хорош их процесс Scrum? Продолжайте опираться на процесс, пока не поймете, что вам нужно что-то больше.

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

19. Собеседования почти бесполезны в смысле понимания, насколько хорошим членом команды будет человек
Интервью гораздо лучше проводить, пытаясь понять, кто человек такой и насколько он заинтересован в данной области знаний. Пытаться выяснить, насколько хорошим членом команды он будет, бесполезно. То, насколько человек умён или хорошо осведомлён, также не является хорошим показателем того, что он будет отличным членом команды. Никто не скажет вам в интервью, что он будет ненадёжным, грубым или никогда не будет приходить на встречу вовремя. Некоторые говорят, что есть «сигналы» для таких вещей… «Если человек спрашивает об отгулах на первом собеседовании, то это всё, что его волнует!» Но все это ерунда. Если вы используете такие сигналы, вы просто угадываете и отвергаете хороших кандидатов.

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

Буду рад, если в комментариях вы поделитесь мудростью, которую вы почерпнули за свою карьеру.

Источник: https://www.simplethread.com/20-things-ive-learned-in-my-20-years-as-a-software-engineer/
Автор оригинала: Justin Etheredge
👍16
День 1508. #ЗаметкиНаПолях
Создавайте Аварийные Пути в Вашем ПО
Вы должны проверять входные данные, предполагать, что они враждебны, перепроверять их на каждом слое и т. д. Это принципы написания хорошего кода. Когда всё идёт гладко, хочется запретить недопустимые операции и опасные действия. Проблемы возникают, когда что-то идёт не так.

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

Допустим, истекает срок действия корневого сертификата, а значит, аутентификации нет. Вы не можете аутентифицироваться на серверах, потому что срок действия используемого вами сертификата аутентификации также истёк. Вам нужен физический доступ, но дата-центр вас не пустит, так как вы не можете аутентифицироваться. Это не фантастика, это случилось недавно в Facebook (не сертификаты, а плохая конфигурация IP, но суть та же).

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

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

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

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

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

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

Источник: https://ayende.com/blog/198625-A/building-emergency-pathways-in-your-software-never-to-be-used
👍7
This media is not supported in your browser
VIEW IN TELEGRAM
День 1509. #ЧтоНовенького
Публичная Бета-версия GitHub Markdown Helpers
В публичной бета-версии вышли функции-помощники Markdown на основе Slash-команд. Просто введите / в описании/комментариях задачи, пул-реквесте или обсуждений и используйте возникающее диалоговое окно, чтобы выбрать из ряда полезных ярлыков Markdown. См. видео.

На данный момент включено 6 функций:
- Блок кода /code
Подсветка синтаксиса для конкретного языка.
- Подробности /details
Сворачиваемая область.
- Сохранённые ответы /saved-replies
Выбор из сохранённых ответов пользователя.
- Таблица /table
Таблица. Можно выбрать количество строк и столбцов.
- Шаблон /template
Шаблон из репозитория. Эта команда будет работать для шаблонов задач и шаблонов пул-реквестов.
- Список заданий* /tasklist
Вставляет список задач (работает только в описании задачи).
*В настоящее время находятся в закрытой бета-версии.

Документация по Slash-командам находится здесь. А здесь можно поучаствовать в дискуссии и оставить отзыв.

Источник
👍11
День 1510. #ЗаметкиНаПолях
Создание Параметризованных Тестов в xUnit
xUnit использует атрибуты для определения методов тестирования. Атрибут Fact определяет простой тест, а атрибут Theory определяет параметризованный тест. Допустим, у нас есть следующий тест, проверяющий, что парсер email корректно извлекает домен:
public void Should_Return_Domain(
string email, string expectedDomain)
{
var parser = new EmailParser();
var domain = parser.GetDomain(email);
Assert.Equal(domain, expectedDomain);
}

Рассмотрим 4 способа написания параметризованных тестов.
1. InlineData
Простейший способ - вы предоставляете тестовые данные, передавая значения конструктору:
[Theory]
[InlineData("[email protected]", "test.com")]
[InlineData("[email protected]", "github.io")]
public void Should_Return_Domain(
string email, string expectedDomain)
{ … }

Мы предоставляем два строковых значения для атрибута InlineData: аргумент тестируемого метода и ожидаемый результат. Можно указать атрибут InlineData столько раз, сколько будет тестовых случаев.
Недостаток в том, что код становится очень многословным, когда у нас много тестовых случаев. И мы ограничены использованием только константных данных для параметров.

2. MemberData
С помощью MemberData мы можем программно предоставить тестовые данные из статического свойства или члена типа:
[Theory]
[MemberData(nameof(EmailTestData))]
public void Should_Return_Domain(
string email, string expectedDomain)
{ … }

public static IEnumerable<object[]>
EmailTestData => new List<object>
{
new object[] { "[email protected]", "test.com" },
new object[] { "[email protected]", "github.io" }
};

Вы указываете имя члена в атрибуте MemberData. Одно ограничение - свойство (или метод) должно возвращать IEnumerable<object[]>, поэтому строгой типизации не существует.

3. ClassData
Позволяет извлечь тестовые данные в отдельный класс. Это полезно для организации тестовых данных отдельно от тестов и упрощает повторное использование. Вы загружаете тестовые данные из класса, который наследуется от IEnumerable<object[]> и реализует метод GetEnumerator:
[Theory]
[ClassData(typeof(EmailTestData))]
public void Should_Return_Domain(
string email, string expectedDomain)
{ … }

public class EmailTestData : IEnumerable<object[]>
{
public IEnumerable<object[]> GetEnumerator()
{
yield return new object[] {
"[email protected]", "test.com" };
yield return new object[] {
"[email protected]", "github.io" };
}

IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
};

Этот подход сложен, потому что нужно реализовать интерфейс IEnumerable. И мы всё ещё страдаем от отсутствия безопасности типов.

4. TheoryData
Позволяет реализовать класс для тестовых данных, сохраняя при этом безопасность типов. Вот пример в сочетании с атрибутом ClassData, но его также можно использовать и с MemberData, возвращая TheoryData из свойства или метода:
[Theory]
[ClassData(typeof(EmailTestData))]
public void Should_Return_Domain(
string email, string expectedDomain)
{ … }

public class EmailTestData :
TheoryData<string, string>
{
public EmailTestData()
{
Add("[email protected]", "test.com");
Add("[email protected]", "github.io");
}
};

TheoryData – обобщённый класс, который позволяет указывать типы для параметризованного теста. Вы просто вызываете метод Add в конструкторе, чтобы предоставить тестовые данные для одного тестового примера. А добавление дополнительных тестов сводится к многократному вызову метода Add.

Источник: https://www.milanjovanovic.tech/blog/creating-data-driven-tests-with-xunit
👍20
День 1511. #Алгоритмы
Использование Префиксного Дерева для Текстового Поиска
Сегодня рассмотрим алгоритм префиксного дерева (Trie) и способы его использования в C# для эффективного поиска текстовых шаблонов.
Trie (произносится «трай») — это древовидная структура данных, которая часто используется для быстрого хранения и извлечения строк. Она состоит из узлов, представляющих отдельные символы в строке. Корневой узел – пустая строка, а каждый дочерний представляет символ, который может следовать за строкой, представленной его родителем.

Преимущества
1) Быстрое определение, присутствует ли данная строка в наборе строк. Строка представлена в виде уникального пути в дереве, что позволяет легко определить её наличие или отсутствие.
2) Способность быстро находить все строки с заданным префиксом. Все строки с общим префиксом имеют один и тот же путь в дереве до конечной точки префикса.

Рассмотрим слова «cat» и «car».
Сохранение: Корневой узел пустой. Первый символ в обеих строках — «c», поэтому добавляем дочерний узел. Аналогично добавляем дочерний узел для «а». Наконец, добавляем два дочерних узла из узла «a» для представления символов «t» и «r».
Поиск: Теперь можно легко определить, присутствует ли в дереве, например, строка «cab». А подсчёт всех строк с префиксом «ca» — это просто количество дочерних узлов для узла «c» > «a».

Реализуем структуру данных Trie. Это набор узлов, поэтому начнём с создания класса TrieNode:
public class TrieNode
{
public bool IsWord { get; set; }
public Dictionary<char, TrieNode>
Children { get; } = new();
}
TrieNode хранит флаг, указывающий, представляет ли узел конец слова в свойстве IsWord, а также словарь дочерних узлов.

Теперь создадим класс Trie, который хранит ссылку на корневой узел и предоставляет методы для вставки и поиска слов:
public class Trie
{
private readonly TrieNode _root = new();

public void AddWord(string word)
{
var node = _root;
foreach (char c in word)
{
if (!node.Children.ContainsKey(c))
node.Children[c] = new();
node = node.Children[c];
}
node.IsWord = true;
}

public bool Search(string word)
{
var node = _root;
foreach (char c in word)
{
if (!node.Children.ContainsKey(c))
return false;
node = node.Children[c];
}
return node.IsWord;
}
}

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

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

Использование
var trie = new Trie();
var text = "The quick brown fox jumps over the lazy dog";
foreach (var word in text.Split(' '))
trie.AddWord(word);

Console.WriteLine(trie.Search("quick"));

Создаём экземпляр Trie и добавляем каждое слово предложения в него с помощью метода AddWord(). Далее с помощью метода Search() проверяем существование слова.

Источник: https://code-maze.com/csharp-using-trie-class-for-efficient-text-pattern-searching/
👍15
День 1512. #ЧтоНовенького
Новинки
ASP.NET Core 8 Превью 2. Начало
Недавно Microsoft выпустила превью 2 .NET 8, в которую включены несколько улучшений в ASP.NET Core. Вот самые заметные.

1. QuickGrid — высокопроизводительный компонент сетки, который позволяет разработчикам отображать данные в табличном формате с расширенными функциями, такими как сортировка, фильтрация, разбиение по страницам и виртуализация. Чтобы его использовать необходима ссылка на пакет Microsoft.AspNetCore.Components.QuickGrid. Этот компонент ранее был экспериментальным пакетом для .NET 7 и претерпел изменения и улучшения API для версии .NET 8.
Если вы использовали QuickGrid в .NET 7, чтобы обновиться до .NET 8, придётся внести некоторые изменения:
- переименовать атрибут Value в State
- переименовать атрибут IsDefaultSort в InitialSortDirection,
- добавить IsDefaultSortColumn=true,
- удалить атрибут ResizableColumns.
На демо-странице https://aspnet.github.io/quickgridsamples/ можно попробовать QuickGrid в действии.

2. Улучшена производительность Blazor WebAssembly с помощью jiterpreter.
jiterpreter — это новая функция среды выполнения в .NET 8, которая обеспечивает частичную поддержку JIT в интерпретаторе .NET IL для повышения производительности среды выполнения.
Приложения Blazor WebAssembly могут запускать код .NET в браузере благодаря небольшой среде выполнения .NET, реализованной в WebAssembly, которая загружается вместе с приложением. Эта среда выполнения представляет собой интерпретатор .NET IL, который является полностью функциональным и небольшим по размеру, но ему не хватает преимуществ производительности выполнения нативного кода посредством JIT-компиляции. JIT для WebAssembly требует создания новых модулей WebAssembly на лету и создания их экземпляров, что создает проблемы для среды выполнения. Вместо этого приложения Blazor WebAssembly могут выбрать предварительную компиляцию (AOT) в WebAssembly, чтобы повысить производительность во время выполнения, но за счёт гораздо большего размера загрузки. Поскольку некоторые распространённые шаблоны кода .NET несовместимы с AOT, интерпретатор .NET IL по-прежнему необходим в качестве резервного механизма для поддержания полной функциональности.

jiterpreter оптимизирует выполнение байт-кодов интерпретатора, заменяя их крошечными фрагментами кода WebAssembly. Используя интерпретатор в качестве основы, мы можем оптимизировать наиболее важные части приложения без необходимости обрабатывать более сложные или непонятные случаи и без чрезмерного усложнения среды выполнения. Хотя jiterpreter не является полной реализацией JIT, он значительно повышает производительность во время выполнения без увеличения размера и времени сборки AOT. jiterpreter также помогает при использовании AOT, оптимизируя случаи, когда среда выполнения должна отступать от интерпретатора. Таким образом, jiterpreter может значительно ускорить выполнение низкоуровневых операций. В превью 2 .NET 8 jiterpreter автоматически включен.

Разработчики, которые хотят использовать ASP.NET Core с превью 2 .NET 8, должны сначала установить пакет SDK для .NET 8. Пользователям Visual Studio для Windows рекомендуется загрузить последнюю предварительную версию VS 2022. Visual Studio для Mac не поддерживает предварительные версии .NET 8 в настоящее время.

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

Источник:
https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-8-preview-2/
👍12
День 1513. #ЗаметкиНаПолях
Таймеры в .NET
В .NET есть как минимум 6 разных таймеров! Каждый для своей цели и варианта использования. Рассмотрим различия между ними.

Во-первых, есть специальные таймеры пользовательского интерфейса. Эти таймеры используются для выполнения кода в UI-потоке:
- System.Windows.Forms.Timer
- System.Windows.Threading.DispatcherTimer

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

Для WebForms есть ещё один таймер: System.Web.UI.Timer. Он генерирует событие обратной передачи на сервере. Но не будем об устаревших технологиях)))

Наконец, 3 таймера, которые не зависят от пользовательского интерфейса.

1. System.Threading.Timer
Самый простой. Он планирует обратный вызов в ThreadPool. Если обработчику требуется больше времени для выполнения, чем интервал, обработчик будет выполнен снова, и вы получите несколько обработчиков, работающих параллельно.
var timer = new System.Threading.Timer(
// обратный вызов может быть выполнен параллельно
// если предыдущий ещё не завершился
// до старта следующего
callback: state => Console.WriteLine("tick"),

// Используется для передачи данных
// в метод обратного вызова,
// чтобы не использовать замыкание
state: null,

// Начать немедленно
dueTime: TimeSpan.Zero,

// Повторять каждую секунду
period: TimeSpan.FromSeconds(1));

// Поставить таймер на паузу
timer.Change(
dueTime: Timeout.Infinite,
period: Timeout.Infinite);

2. System.Timers.Timer
Использует System.Threading.Timer внутри и предоставляет несколько дополнительных функций, таких как AutoReset, Enabled или SynchronizingObject, которые позволяют настроить способ выполнения обратного вызова. Также событие Tick позволяет зарегистрировать несколько обработчиков. Вы также можете изменить обработчик после запуска таймера.
var timer = new System.Timers.Timer(
TimeSpan.FromSeconds(1));

// несколько обработчиков
timer.Elapsed += (sender, e)
=> Console.WriteLine("Handler 1");
timer.Elapsed += (sender, e)
=> Console.WriteLine("Handler 2");

// Останавливаем после 1го выполнения
timer.AutoReset = false;

// Запускаем
timer.Start();

3. System.Threading.PeriodicTimer
Используется в цикле и поддерживает асинхронные обработчики. У него нет события Tick, но он предоставляет метод WaitForNextTickAsync, возвращающий ValueTask<bool>, которая завершается к концу интервала. Булев параметр указывает, был ли таймер удалён. Таким образом, можно использовать его в цикле while и обратные вызовы не могут перекрываться.
using var cts = 
new CancellationTokenSource();
using var timer =
new PeriodicTimer(TimeSpan.FromSeconds(1));

while (
await timer.WaitForNextTickAsync(cts.Token))
{
Console.WriteLine("Tick");
await AsyncOperation();
}

Источник: https://www.meziantou.net/too-many-timers-in-dotnet.htm
👍15