.NET Разработчик
6.55K subscribers
442 photos
3 videos
14 files
2.12K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 2086. #Docker
Развенчиваем Мифы о Docker. Начало
В 2013м Docker произвёл революцию, представив портативную, удобную для пользователя платформу контейнеров, получившую широкое распространение. Хотя Docker Desktop является ведущим инструментом для создания контейнеризированных приложений, Docker по-прежнему окружён многочисленными заблуждениями. В этой серии развенчаем главные мифы о Docker и рассмотрим его возможности и преимущества.

Миф 1: Docker больше не является продуктом с открытым кодом
Docker состоит из нескольких компонентов, большинство из которых являются продуктами с открытым кодом. Основной Docker Engine и другие важные части экосистемы Docker, такие как Docker CLI и Docker Compose, остаются продуктами с открытым кодом.

В 2017 году из тогдашней монолитной кодовой базы Docker был выделен проект Moby, чтобы предоставить набор «строительных блоков» для создания контейнерных решений и платформ. Docker использует Moby для бесплатного проекта Docker Engine и коммерческого Docker Desktop.

Пользователи также могут найти проверенный контент с открытым кодом на Docker Hub: спонсируемые Docker образы и официальные образы Docker.

Миф 2: Docker-контейнеры — это виртуальные машины
Docker-контейнеры часто ошибочно принимают за виртуальные машины (VM), но эти технологии работают совершенно по-разному. В отличие от VM, Docker-контейнеры не включают в себя всю операционную систему. Они совместно используют ядро операционной системы хоста, что делает их более лёгкими и эффективными. Для виртуальных машин требуется гипервизор для создания виртуального оборудования для гостевой ОС, что приводит к значительным накладным расходам. Docker упаковывает только приложение и его зависимости.

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

Однако при работе на системах, отличных от Linux, Docker должен эмулировать среду Linux. Например, Docker Desktop использует полностью управляемую виртуальную машину для обеспечения единообразной работы в Windows, Mac и Linux, запуская свои компоненты Linux внутри этой виртуальной машины.

Миф 3: Docker Engine, Docker Desktop и Docker Enterprise Edition — одно и то же
Значительная путаница окружает различные доступные варианты Docker, которые включают в себя:
- Mirantis Container Runtime
Docker Enterprise Edition (Docker EE) был продан Mirantis в 2019 году и переименован в Mirantis Container Runtime. Это платное ПО предназначено для развёртывания производственных контейнеров и предлагает легкую альтернативу существующим инструментам оркестровки.
- Docker Engine
Полностью открытая версия, созданная на основе проекта Moby, предоставляющая Docker Engine и CLI.
- Docker Desktop
Коммерческое ПО, продаваемое Docker, которое объединяет Docker Engine с дополнительными функциями для повышения производительности разработчиков. Подписка Docker Business включает расширенные функции безопасности и управления для предприятий.

Эти варианты отличаются в основном функциями и возможностями. Docker Engine обслуживает сообщество разработчиков с открытым кодом, Docker Desktop улучшает рабочие процессы разработчиков с помощью комплексного набора инструментов для создания и масштабирования приложений, а Mirantis Container Runtime предоставляет специализированное решение для корпоративных производственных сред с расширенным управлением и поддержкой.

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

Источник:
https://www.docker.com/blog/docker-myths-debunked/
👍12
День 2087. #Docker
Развенчиваем Мифы о Docker. Продолжение

Начало

Миф 4: Docker — то же самое, что Kubernetes
Этот миф возникает из-за того, что и Docker, и Kubernetes связаны с контейнеризированными средами. Но они выполняют разные роли.

Kubernetes (K8s) — система оркестровки для управления экземплярами контейнеров в масштабе. Он автоматизирует развёртывание, масштабирование и эксплуатацию нескольких контейнеров в кластерах хостов. Другие технологии оркестровки включают Nomad, бессерверные фреймворки, режим Swarm в Docker и Apache Mesos. Каждая из них предлагает различные функции для управления контейнеризированными рабочими нагрузками.

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

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

Миф 5: Docker не безопасен
Такое убеждение часто возникает из-за недопонимания того, как реализована безопасность в Docker. Чтобы помочь снизить уязвимости безопасности Docker предлагает следующие меры:
- Конфигурация безопасности по выбору
За исключением нескольких компонентов, Docker работает на основе добровольного включения безопасности. Такой подход устраняет помехи для новых пользователей, но означает, что Docker все равно можно настроить для большей безопасности с точки зрения корпоративных требований и для пользователей, заботящихся о безопасности и имеющих конфиденциальные данные.
- Возможности режима «Rootless»
Docker Engine может работать в режиме rootless, в котором демон Docker работает без прав root. Это уменьшает потенциальный радиус поражения вредоносного кода, покидающего контейнер и получающего права root на хосте. Docker Desktop также предлагает улучшенную изоляцию контейнера (ECI).
- Встроенные функции безопасности
Также Docker включает встроенные функции, такие как пространства имен, группы управления (cgroups) и профили seccomp, которые обеспечивают изоляцию и ограничивают возможности контейнеров.
- Аттестация SOC 2 Type 2 и сертификация ISO 27001
Как инструмент с открытым исходным кодом Docker Engine не подпадает под эти сертификации. Они относятся к платным продуктам Docker, Inc., которые предлагают дополнительные функции безопасности корпоративного уровня.

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

Миф 6: Docker мёртв
Этот миф проистекает из быстрого роста и изменений в экосистеме контейнеров за последнее десятилетие. Docker активно разрабатывается и также широко применяется. Docker Hub - одно из крупнейших в мире хранилищ образов контейнеров.
С развитием науки о данных и AI/ML образы Docker облегчают обмен моделями и приложениями, и поддерживаются возможностями рабочей нагрузки GPU в Docker Desktop. Кроме того, Docker широко используется для быстрого и экономически эффективного макетирования тестовых сценариев в качестве альтернативы развёртыванию реального оборудования или виртуальных машин.

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

Источник:
https://www.docker.com/blog/docker-myths-debunked/
👍9
День 2088. #Docker
Развенчиваем Мифы о Docker. Окончание

Начало
Продолжение

Миф 7: Docker сложно изучить
Такое мнение часто возникает из-за предполагаемой сложности концепций контейнеров и многочисленных функций Docker. Однако Docker является популярной технологией, используемой более чем 20 миллионами разработчиков по всему миру, и доступно бесчисленное множество ресурсов, чтобы сделать изучение Docker доступным.

Docker, Inc. стремится создавать интуитивно понятный и удобный дизайн для Docker Desktop и вспомогательных продуктов. Документация, семинары, тренинги, руководства и примеры доступны на сайте Docker Desktop, в блогах, а также в подписках на новости. А курсы Udemy, созданные совместно с Docker, помогают новым пользователям понять контейнеризацию и использование Docker.

Миф № 8: Docker и контейнеры предназначены только для разработчиков
Docker и контейнеры используются в различных областях, помимо разработки:
- Data science
Docker предоставляет согласованные среды, позволяя специалистам по данным беспрепятственно обмениваться моделями, наборами данных и настройками разработки.
- Здравоохранение
Docker развёртывает масштабируемые приложения для управления данными пациентов и запуска симуляций, таких как ПО для медицинской визуализации, в различных больничных системах.
- Образование
Преподаватели и студенты используют Docker для создания воспроизводимых «песочниц», которые облегчают совместную работу и упрощают настройки проектов кодирования.
Универсальность Docker выходит за рамки разработки, предоставляя согласованные, масштабируемые и безопасные среды для различных приложений.

Миф № 9: Docker Desktop — всего лишь графический интерфейс
Этот миф игнорирует его обширные функции, разработанные для улучшения опыта разработчиков, упрощения управления контейнерами и повышения производительности:
- Поддержка кроссплатформенности
Docker работает на базе Linux, но большинство рабочих станций разработчиков работают под управлением Windows или macOS. Docker Desktop позволяет этим платформам запускать инструменты Docker внутри полностью управляемой виртуальной машины, интегрированной с сетями, файловой системой и ресурсами хост-системы.
- Инструменты разработчика
Встроенные Kubernetes, Docker Scout для управления цепочкой поставок, Docker Build Cloud для более быстрой сборки и Docker Debug для отладки контейнеров.
- Безопасность и управление
Для администраторов Docker Desktop предлагает управление доступом к реестру и к образам, улучшенную изоляцию контейнеров, единый вход (SSO) для авторизации и управление настройками, и т.д.

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

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

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

Источник: https://www.docker.com/blog/docker-myths-debunked/
👍15
День 2089. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 28. Не меняйте оценку в зависимости от того, что хочет услышать получатель
Предположим, клиент спрашивает, сколько времени вам потребуется, чтобы завершить следующую часть вашего проекта. Основываясь на анализе прошлого опыта, вы отвечаете: «Около двух месяцев».
«Два месяца? — усмехается клиент. — Моя 12-летняя дочь сделает это за три недели!»
В ответ вы, сопротивляясь искушению предложить ему нанять свою дочь, делаете вторую попытку: «Хорошо, как насчет одного месяца?»

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

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

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

Более рациональным подходом для разработчика было бы сказать: «Я пришёл к своей оценке согласно таким-то и таким-то расчётам. А как вы получили свою?» Скорей всего, у менеджера не было никакой оценки — у него была цель. У разработчика тоже не было оценки — у было предположение. Цель и предположение оказались далеки друг от друга. Если бы одна из двух сторон разработала верную оценку, то они могли бы быть ближе друг к другу. Вместо этого обсуждение превратилось в напряжённый спор, и человек, обладавший большей властью, выиграл его, получив обещание от другого.

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

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
👍13
День 2090. #юмор
👍14
Все зависимости вашего класса определены как интерфейсы. Конкретные реализации интерфейсов (зависимости) внедряются в ваш класс через конструктор. С какими функциями (свойствами и методами) этих зависимостей может работать ваш класс?
#Quiz #BestPractices
Anonymous Quiz
31%
С функциями, определёнными в любом из интерфейсов, которые реализует внедрённая зависимость
13%
Со всеми функциями конкретного типа внедрённой зависимости
53%
Только с функциями интерфейса зависимости
3%
Только с функциями класса зависимости, но не с функциями, определёнными в интерфейсе
День 2091. #ЗаметкиНаПолях
Task.Factory.StartNew и Длительные Асинхронные Задачи
Допустим, вы хотите реализовать шаблон «производитель-потребитель» на основе каналов для асинхронной обработки элементов:
public class Processor
{
private Channel<string> _channel =
Channel.CreateUnbounded<string>();
private Task _task;

public Processor()
{
_task = Task.Factory.StartNew(async () =>
{
await foreach (var log in
_channel.Reader.ReadAllAsync())
{
// Обработка
Console.WriteLine(log);
}
}, TaskCreationOptions.LongRunning);
}

public void ProcessLog(string log)
{
_channel.Writer.TryWrite(log);
}
}

И поскольку вы знаете, что задача обработки должна выполняться в течение всего процесса, вы используете флаг TaskCreationOptions.LongRunning.

Как думаете, есть здесь проблема? На самом деле, да.

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

Давайте упростим код и добавим немного трассировки:
static void WriteLine(string msg)
{
Console.WriteLine(
$"""
[{Environment.CurrentManagedThreadId}]
[IsThreadPoolThread: {Thread.CurrentThread.IsThreadPoolThread}]
{msg}.
"""
);
}

var task = Task.Factory.StartNew(async () =>
{
WriteLine("Task started");
await Task.Delay(1000);
WriteLine("Task completed");
}, TaskCreationOptions.LongRunning);

Thread.Sleep(100);
Console.WriteLine(
$"Completed: " + task.IsCompleted);

Console.ReadLine();

Вывод:
[4]
[IsThreadPoolThread: False]
Task started.
Completed: True
[6]
[IsThreadPoolThread: True]
Task completed.

Вот что происходит во время выполнения:
- Task started печатается из выделенного потока.
- Task completed печатается из потока пула потоков.

Задача, по-видимому, завершена до выполнения обратного вызова. Это происходит, потому что Task.Factory.StartNew не является асинхронно-дружественным. Фактический тип переменной задачи — Task<Task>, а родительская задача завершается, когда новый поток начинает выполнять обратный вызов, а не когда завершается сам обратный вызов.

Флаг LongRunning учитывается для запуска первого блока асинхронного метода перед первым await. Await приостанавливает выполнение асинхронного метода, а остальная часть метода планируется в потоке пула потоков планировщиком задач по умолчанию.

Теоретически возможно, что обратный вызов, который вы предоставляете StartNew, имеет длительную часть перед первым await, и вы действительно хотите запустить его в выделенном потоке. Если это так, флаг LongRunning допустим, но это очень редкое явление.

Рекомендации для Task.Factory.StartNew и асинхронных делегатов:
1. Избегайте использования Task.Factory.StartNew с асинхронными делегатами. Если необходимо, используйте метод расширения Unwarp, чтобы получить фактическую базовую задачу.
2. Не используйте флаг LongRunning с Task.Factory.StartNew для асинхронных обратных вызовов. Флаг полезен для синхронных методов, которые блокируют поток, но не для асинхронных методов, где продолжение будет запланировано в пуле потоков.

Источник: https://sergeyteplyakov.github.io/Blog/csharp/2024/10/01/Task.Factory.StartNew-and-long-running-async-tasks.html
👍24
День 2092. #ЧтоНовенького
Разбираем Атрибут OverloadResolutionPriority в C# 13

C# 13 добавляет поддержку нового атрибута OverloadResolutionPriority. Этот атрибут является узкоспециализированным, поэтому большинство из вас не будет его использовать. Тем не менее, это приятное дополнение к языку, и о нём стоит знать, если вы пишете библиотеки.

Рассмотрим простой пример:
Sample.Test(); // Вывод: Test 1

static class Sample
{
public static void Test()
=> Console.WriteLine("Test 1");
}

Если вы захотите обновить сигнатуру и добавить новый необязательный параметр:
Sample.Test(); // Вывод: Test 1

static class Sample
{
public static void Test(
[CallerMemberName]string name = "")
=> Console.WriteLine("Test 1");
}

Но это ломает бинарный код, поскольку метода без параметров больше не существует. Лучшим решением было бы добавление перегрузки, но это не сработает:
Sample.Test(); // Вывод: Test 1

static class Sample
{
public static void Test()
=> Console.WriteLine("Test 1");

public static void Test(
[CallerMemberName]string name = "")
=> Console.WriteLine("Test 2");
}

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

Обратите внимание, что удаление первой перегрузки сломает бинарный код. Вы не можете это делать в библиотеке. Новый атрибут OverloadResolutionPriority позволяет указать приоритет разрешения перегрузки для компилятора. Компилятор выберет совместимую перегрузку с наивысшим приоритетом.
Sample.Test(); // Вывод: Test 2

static class Sample
{
[OverloadResolutionPriority(-1)]
public static void Test()
=> Console.WriteLine("Test 1");

public static void Test(
[CallerMemberName]string name = "")
=> Console.WriteLine("Test 2");
}

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

Вы также можете использовать этот атрибут для приоритизации некоторых перегрузок, которые требуют неявных преобразований:
Sample.Test(100);  // Вывод: byte
Sample.Test(1000); // Вывод: int

static class Sample
{
public static void Test(int a)
=> Console.WriteLine("int");

[OverloadResolutionPriority(1)]
public static void Test(byte a)
=> Console.WriteLine("byte");
}


Источник: https://www.meziantou.net/understanding-overloadresolutionpriority-attribute-in-csharp-13.htm
👍29
День 2093. #Курсы
Центр Обучения Copilot
Copilot меняет способ, которым много людей выполняют ежедневные задачи. Copilot, как ИИ-помощник, позволяет нам быть более креативными и эффективными в широком спектре видов деятельности.

Когда Microsoft выпустила Copilot в марте 2023 года, у пользователей возникли вопросы. Они хотели узнать, как они могут использовать Copilot, чтобы быть более продуктивными на работе и в повседневной жизни. Им было интересно, как использовать Copilot в приложениях Microsoft 365, таких как Word, PowerPoint, Outlook и Teams, и как использовать Copilot разработчикам, специалистам по данным и специалистам по безопасности.

Чтобы ответить на эти и другие вопросы, в мае 2024 года создан Центр Обучения Copilot.

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

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

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

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

Источник: https://techcommunity.microsoft.com/t5/microsoft-learn-blog/get-ai-ready-inside-the-copilot-learning-hub/ba-p/4248103
👍4
День 2094. #ЧтоНовенького
Улучшаем Отладку Коллекций с Помощью Редактируемых Выражений
Мне частенько не хватало в отладчике Visual Studio возможности отладки LINQ-выражений или возможности использовать LINQ, чтобы отфильтровать элементы коллекции. Новая функция редактируемых выражений в отладчике Visual Studio позволяет это делать.

Эта функция позволяет использовать текстовое поле выражений в верхней части диалогового окна визуализатора IEnumerable, добавляя в него желаемые LINQ-выражения. Визуализатор обновляется в режиме реального времени, отражая результаты запроса. Можно легко применять различные фильтры или порядки сортировки к коллекциям в зависимости от ваших потребностей.

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

Это откроет диалоговое окно визуализатора IEnumerable, в котором вы увидите текстовое поле выражений вверху. Вы можете ввести любое допустимое выражение LINQ в это текстовое поле и нажать <ENTER>, чтобы применить его к коллекции. Визуализатор обновит сетку данных ниже результатами запроса.

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

Источник: https://devblogs.microsoft.com/visualstudio/improve-your-debugger-game-with-editable-expressions/
👍20
День 2095. #ЗаметкиНаПолях #БазыДанных
Типы Ключей

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

Первичные ключи
Первичный ключ — это поле, которое однозначно идентифицирует каждую запись в таблице в реляционной БД. Они делятся на 2 группы:
- Естественные - существующие данные, например, серия и номер паспорта человека, VIN-номер машины, ISBN книги и т.п.
- Суррогатные (синтетические) - сгенерированы специально для использования в БД и не имеют представления среди реальных атрибутов сущности. Нередко используется автоинкрементное поле для автоматического назначения уникального номера каждой записи в таблице.

См. также «Вы Пожалеете, что Использовали Естественные Ключи»

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

Столбец идентификатора клиента может существовать:
- в таблице клиентов как первичный ключ
- в таблице выданных книг как внешний ключ
Столбец идентификатора книги может существовать:
- в таблице книг как первичный ключ
- в таблице выданных книг как внешний ключ

Необходимо соблюдать особую осторожность при вставке и удалении данных из столбца внешнего ключа. Неосторожное удаление или вставка может разрушить связь между двумя таблицами. Чтобы избежать этого, в большинстве СУБД есть правила ограничений:
- вставка - запрет вставки записи со значением внешнего ключа, которое отсутствует как значение первичного ключа в связанной таблице;
- обновление – если существуют связанные записи с таким значением внешнего ключа: запрет обновления, каскадное обновление связанных записей или установка значений ключа в NULL у связанных записей (поле должно допускать NULL);
- удаление - если существуют связанные записи с таким значением внешнего ключа: запрет удаления, каскадное удаление связанных записей или установка значений ключа в NULL у связанных записей (поле должно допускать NULL).

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

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

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

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

Источник: https://www.bbc.co.uk/bitesize/guides/z4wf8xs/revision/4
👍16
День 2096. #ЧтоНовенького
WebStorm и Rider Теперь Бесплатны для Некоммерческого Использования
Думаю, все уже знают об этой новости, но, всё-таки, не могу не упомянуть.

JetBrains объявили об изменении модели лицензирования — WebStorm и Rider теперь бесплатны для некоммерческого использования!

Ранее в этом году бесплатными для некоммерческого использования сделали IDE RustRover и Aqua, теперь распространили эту модель на WebStorm и Rider. Если вы используете эти IDE в некоммерческих целях, таких как обучение, разработка проектов с открытым кодом, создание контента или хобби, теперь вы можете делать это бесплатно.

Для коммерческих проектов ничего не изменится — существующее лицензирование остаётся в силе. Другие IDE JetBrains также не затронуты этим обновлением.

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

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

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

Источник: https://blog.jetbrains.com/blog/2024/10/24/webstorm-and-rider-are-now-free-for-non-commercial-use/
5👍12
Какой(-ими) IDE вы пользуетесь для .NET разработки?
(возможно несколько ответов, если одна рабочая, другая для пет-проектов)
Anonymous Poll
47%
Rider
52%
Visual Studio
13%
Visual Studio + Resharper
27%
VS Code
1%
Другая (напишу в комментариях)
1%
Только консоль, только хардкор!
👍1
День 2097. #УрокиРазработки
Уроки 50 Лет Разработки ПО

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

Определение критического пути
Планировщики проектов часто рисуют сетевой график (также называемый диаграммой PERT), чтобы показать временные отношения между задачами. На рисунке выше показан такой сетевой график для проекта с шестью задачами, от A до Е. Показана расчётная продолжительность каждой задачи. Сетевые графики содержат гораздо больше информации, например самые ранние и самые поздние даты начала и окончания каждой задачи. Для простоты предположим, что нельзя приступить ни к одной задаче, пока не будут выполнены все предшествующие ей. Т.е. задача Е не может начаться, пока не будут выполнены задачи A, Г и Д.

Если суммировать приблизительную продолжительность задач в различных путях, то можно заметить, что путь ДЕ самый длинный: 5 + 4 = 9 дней (жирные стрелки). Эта последовательность задач является критическим путём и определяет кратчайшую расчётную продолжительность реализации проекта. Если на реализацию какой-либо задачи в критическом пути потребуется больше времени, то дата завершения всего проекта сдвинется на время этой задержки. Новые задачи, добавляемые в критический путь, тоже задерживают завершение проекта.

Задачи, не лежащие на критическом пути, имеют резервное время. Например, задачи A и Г вместе имеют один резервный день: 2 + 2 = 4 дня, что на 1 день меньше, чем продолжительность решения задачи Д, лежащей на критическом пути.

Однако критический путь может измениться. Если на решение задачи А потребовалось 4 дня, что вдвое превышает первоначальную оценку, критическим станет путь АГЕ протяженностью 10 дней (4 + 2 + 4), в результате чего срок окончания проекта на 1 день превысит путь ДЕ.

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

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

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

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

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
👍12
День 2098. #Git #TipsAndTricks
Советы по Git: Предыдущие Ветки

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

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

Не зная точного имени ветки, вы можете использовать:
git checkout -

или (начиная с версии Git 2.23):
git switch -


На самом деле, это сокращённая версия следующей команды:
git checkout @{-1}

Эта команда позволяет вернуться к любой предыдущей ветке.

Получение последних 5 использованных веток
Если вы хотите увидеть последние 5 веток, которые вы использовали, вы можете выполнить следующую команду:
git reflog | grep -o 'checkout: moving from [^ ]* to [^ ]*' | awk '{print $NF}' | awk '!seen[$0]++' | head -n 5 

Это работает с git bash и такими ОС, как Linux и MacOS. Идея заключается в использовании reflog для получения истории веток, которые вы извлекали. Команда grep фильтрует вывод, чтобы показывать только ветки, которые вы извлекли. Первая команда awk извлекает имена веток из вывода. Вторая команда awk удаляет дубликаты. Команда head ограничивает вывод последними 5 ветками. Поэтому, если вам нужны только последние 3 ветки, которые вы извлекли, вы можете изменить head -n 5 на head -n 3.

Источник: https://steven-giesel.com/blogPost/bbfb8333-e05a-4de7-88b9-17ac2248d77f/git-tricks-get-the-last-checked-out-branch
👍19
День 2099. #ЗаметкиНаПолях
Реализация Идемпотентных POST-запросов в
ASP.NET Core API
Идемпотентность — важнейшая концепция REST API, которая обеспечивает надёжность и согласованность системы, особенно в распределённых системах, где сетевые проблемы могут привести к повторным запросам. Реализуя идемпотентность, вы предотвращаете дублирование операций, которое может возникнуть из-за повторных попыток клиента.

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

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

Примечание: если запрос не удается (возвращает 4xx/5xx), мы не кэшируем ответ. Это позволяет клиентам повторять попытки с тем же ключом идемпотентности. Однако это означает, что неудавшийся запрос, за которым следует успешный запрос с тем же ключом, будет успешным — убедитесь, что это соответствует вашим бизнес-требованиям.

Мы можем реализовать идемпотентность для контроллеров с помощью атрибута фильтра действия, реализовав IAsyncActionFilter:
[AttributeUsage(AttributeTargets.Method)]
internal sealed class IdempotentAttribute :
Attribute, IAsyncActionFilter
{
private readonly TimeSpan _cacheMins;

public IdempotentAttribute(int cacheMins = 60)
{
_cacheMins = TimeSpan.FromMinutes(cacheMins);
}

public async Task OnActionExecutionAsync(
ActionExecutingContext ctx,
ActionExecutionDelegate next)
{
// проверяем, передал ли клиент ключ
if (!ctx.HttpContext.Request
.Headers.TryGetValue(
"Idempotence-Key",
out var keyValue) ||
!Guid.TryParse(keyValue, out Guid key))
{
ctx.Result = new BadRequestObjectResult(
"Отсутствует заголовок Idempotence-Key");
return;
}

var cache = ctx.HttpContext
.RequestServices
.GetRequiredService<IDistributedCache>();

// проверяем, обработан ли ключ
var cacheKey = $"Idempotent_{key}";
var cachedResult = await
cache.GetStringAsync(cacheKey);
if (cachedResult is not null)
{
var response = JsonSerializer
.Deserialize<IdempotentResponse>(cachedResult);

var result = new ObjectResult(response.Value) {
StatusCode = response.StatusCode };

ctx.Result = result;

return;
}

// выполняем действие и кэшируем результат
var executedContext = await next();

if (executedContext.Result is
ObjectResult { StatusCode: >= 200 and < 300 } objRes)
{
int status = objRes.StatusCode
?? StatusCodes.Status200OK;
IdempotentResponse response =
new(status, objRes.Value);

await cache.SetStringAsync(
cacheKey,
JsonSerializer.Serialize(response),
new DistributedCacheEntryOptions {
AbsoluteExpirationRelativeToNow = _cacheMins }
);
}
}
}

internal sealed class IdempotentResponse
{
[JsonConstructor]
public IdempotentResponse(
int status, object? value)
{
StatusCode = status;
Value = value;
}

public int StatusCode { get; }
public object? Value { get; }
}

Теперь можно применить атрибут к методу контроллера:
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
[HttpPost]
[Idempotent(cacheMins: 60)]
public IActionResult Create(
[FromBody] CreateOrderRequest request)
{
// обработка заказа…

return CreatedAtAction(nameof(GetOrder),
new { id = orderDto.Id }, orderDto);
}
}


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

Источник
👍28
День 2100. #ЗаметкиНаПолях
Реализация Идемпотентных POST-запросов в
ASP.NET Core API. Окончание
Начало

Идемпотентность в минимальных API
В минимальных API аналогично реализуем IEndpointFilter:
internal sealed class IdempotencyFilter(
int cacheMins = 60) : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(
EndpointFilterInvocationContext ctx,
EndpointFilterDelegate next)
{
// получаем ключ из запроса
if (!ctx.HttpContext.Request
.Headers.TryGetValue(
"Idempotence-Key",
out var keyValue) ||
!Guid.TryParse(keyValue, out Guid key))
{
return Results.BadRequest(
"Отсутствует заголовок Idempotence-Key");
}

var cache = ctx.HttpContext.RequestServices
.GetRequiredService<IDistributedCache>();

// проверяем, обработан ли ключ
var cacheKey = $"Idempotent_{key}";
var cachedResult = await
cache.GetStringAsync(cacheKey);
if (cachedResult is not null)
{
var resp = JsonSerializer
.Deserialize<IdempotentResult>(cachedResult)!;
return new IdempotentResult(
resp.StatusCode, resp.Value);
}

object? result = await next(ctx);

// выполняем действие и кэшируем результат
if (result is IStatusCodeHttpResult
{
StatusCode: >= 200 and < 300
} statusResult
and IValueHttpResult valueResult)
{
var status = statusResult.StatusCode
?? StatusCodes.Status200OK;
IdempotentResult resp =
new(status, valueResult.Value);

await cache.SetStringAsync(
cacheKey,
JsonSerializer.Serialize(resp),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMinutes(cacheMins)
});
}

return result;
}
}


Создаём свой тип результата:
[method: JsonConstructor]
internal sealed class IdempotentResult(
int status, object? value) : IResult
{
public int StatusCode { get; } = status;
public object? Value { get; } = value;

public Task ExecuteAsync(HttpContext ctx)
{
ctx.Response.StatusCode = StatusCode;
return ctx.Response
.WriteAsJsonAsync(Value);
}
}

Теперь применим фильтр к конечной точке минимальных API:
app.MapPost("/api/orders", CreateOrder)
.RequireAuthorization()
.WithOpenApi()
.AddEndpointFilter<IdempotencyFilter>();


Ещё одним вариантом является реализация логики идемпотентности в промежуточном ПО.

Замечания
1. Длительность кэширования — сложная задача. Разумное время - от нескольких минут до 24–48 часов в зависимости от вашего конкретного варианта использования.
2. Между проверкой и установкой значения кэша существует небольшое окно состояния гонки. Это может быть проблемой, особенно в API с высоким трафиком. Потокобезопасная реализация с использованием распределённой блокировки отлично работает. Она контролирует ситуацию, когда одновременно поступает несколько запросов. Но это должно быть редким явлением.
3. Для распределённого кэша идеально подходит Redis. Кроме того, он обрабатывает распределенную блокировку.
4. Что делать, если клиент повторно использует ключ идемпотентности с другим телом запроса? Вернуть ошибку. Можно хэшировать тело запроса и сохранять его с ключом идемпотентности. Если хеш нового запроса с тем же ключом отличается от сохранённого, возвращается ошибка. Это предотвращает неправильное использование ключей идемпотентности и поддерживает целостность вашего API.

Источник: https://www.milanjovanovic.tech/blog/implementing-idempotent-rest-apis-in-aspnetcore
👍10👎1
День 2101. #ЗаметкиНаПолях
Использование Singleton в Многопоточных Сценариях. Начало

Основная идея паттерна Одиночка (Singleton) в том, что это класс, предназначенный для создания только одного экземпляра, и он обычно предоставляет простой способ доступа к этому экземпляру.

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

Рассмотрим варианты его реализации.

1. Базовый — не потокобезопасный
Это канонический пример реализации паттерна Singleton.
public sealed class Singleton
{
private static Singleton instance = null;

private Singleton() {}

public static Singleton Instance
{
get
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
}

Это не потокобезопасно. Может случиться так, что два потока одновременно проверят экземпляр на null и получат true. В результате каждый создаст экземпляр, что противоречит правилу наличия только одного экземпляра Singleton. Также возможно, что экземпляр будет создан до того, как произойдёт эта проверка, но другие потоки могут не увидеть этот новый экземпляр сразу, если не предпринять определённые шаги, чтобы убедиться, что они это сделают.

2. Используем lock
Сначала поток блокирует общий объект, а затем проверяет, был ли уже создан экземпляр. Если нет, он продолжает и создаёт экземпляр. Этот процесс гарантирует, что все чтения будут происходить после того, как будет получена блокировка, а все записи будут происходить до того, как она будет освобождена. Так только один поток сможет создать экземпляр, потому что пока один поток находится в процессе его создания, никакой другой поток не может войти в эту часть кода. К тому времени, как второй поток доберётся туда, первый уже создаст экземпляр, поэтому проверка на null вернёт false.
public sealed class Singleton
{
private static Singleton instance = null;
private static readonly object lck = new object();

Singleton(){}

public static Singleton Instance
{
get
{
lock (lck)
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
}
}

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

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

Источник:
https://thecodeman.net/posts/how-to-use-singleton-in-multithreading
👍5
День 2102. #ЗаметкиНаПолях
Использование Singleton в Многопоточных Сценариях. Окончание

Начало

3. Блокировка с двойной проверкой
Первая проверка: первая проверка на null. Если экземпляр не пуст, он возвращается. Эта проверка позволяет избежать накладных расходов на блокировку в большинстве вызовов после инициализации экземпляра.
Блокировка: если экземпляр null, получается блокировка.
Вторая проверка: внутри блокировки выполняется вторая проверка, т.к. другой поток мог инициализировать экземпляр между первой проверкой и получением блокировки. Если экземпляр всё ещё null после второй проверки, он создаётся.
public sealed class Singleton
{
private static Singleton instance = null;
private static readonly object lck = new object();

Singleton() {}

public static Singleton Instance
{
get
{
if (instance == null)
{
lock (lck)
{
if (instance == null)
instance = new Singleton();
}
}
}
return instance;
}
}

Это лучшее решение? Не совсем. Решение сложное и может вызвать проблемы, если его неправильно использовать.

4. Singleton с Lazy
Ленивая инициализация: класс Lazy автоматически обрабатывает ленивую инициализацию экземпляра Singleton. Экземпляр не создаётся, пока он действительно не понадобится. Lazy гарантирует, что экземпляр создаётся потокобезопасным способом, поэтому не нужно использовать блокировки или дважды проверять блокировку. Кроме того, код проще и удобнее для обслуживания, поскольку сложность синхронизации потоков инкапсулирована в классе Lazy.
public class Singleton
{
private static readonly Lazy<Singleton>
instance = new Lazy<Singleton>(
() => new Singleton());

private Singleton() {}

public static Singleton Instance
=> instance.Value;
}

Ключевые преимущества использования Lazy для реализации Singleton:
• Потокобезопасность: Lazy обрабатывает все сложности потокобезопасности, снижая риск ошибок.
• Производительность: как правило, более эффективно, так как не требует явной блокировки (которая может быть затратной).
• Простота: код намного чище и понятнее, что повышает удобство обслуживания.

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

Источник: https://thecodeman.net/posts/how-to-use-singleton-in-multithreading
👍28