.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
День 1523. #ЗаметкиНаПолях
Обработка CancelKeyPress с Помощью CancellationToken
Иногда нужно определить, когда консольное приложение закрывается, чтобы выполнить некоторую очистку. Console.CancelKeyPress позволяет зарегистрировать метод обратного вызова, который выполнится при нажатии Ctrl+C или Ctrl+Break в консоли. Это событие также позволяет предотвратить закрытие приложения, чтобы вы могли выполнить очистку перед завершением работы. Можно использовать Console.CancelKeyPress в паре с CancellationToken для отмены текущих асинхронных операций.

Метод, выполнение которого прерывается:
static async Task DoAsync (
string[] args, CancellationToken ct)
{
try
{
Console.WriteLine("Ожидание…");
await Task.Delay(10_000, ct);
}
catch (OperationCanceledException)
{
Console.WriteLine("Операция отменена");
}
}

Основной текст программы:
using var cts = new CancellationTokenSource();
Console.CancelKeyPress += (sender, e) =>
{
// Мы остановим процесс вручную
// с помощью токена отмены
e.Cancel = true;

// … очистка …

// Вызываем отмену на токене
cts.Cancel();
};

await DoAsync(args, cts.Token);

Кроме того, у токена отмены есть метод Register, позволяющий зарегистрировать методы обратного вызова, которые выполнятся в случае отмены токена:
cts.Token.Register(
() => Console.WriteLine("Отмена…"));

Источник: https://www.meziantou.net/handling-cancelkeypress-using-a-cancellationtoken.htm
👍8
День 1524. #ЗаметкиНаПолях
Храните Информацию в Её Высшей Форме. Начало
Может быть несколько представлений некоторой части информации; вы не должны ограничивать себя только одним из них. Вместо этого храните источник этой информации.

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

Этот совет можно перефразировать как: «Храните исходник, а не исполнение.»

Звучит тривиально. Вот более сложный пример. Есть сущность Customer и объект-значение LoyaltyPoints:
public class Customer : Entity 
{
public LoyaltyPoints Points { get; private set; }

public void AddPoints(LoyaltyPoints pts)
=> Points += pts;

public void RedeemPoints(LoyaltyPoints pts)
{
if (Points < 250 || pts > Points)
throw new Exception();
Points -= pts;
}
}

У нас два варианта использования:
1) Когда клиент размещает заказ, объект Order вычисляет баллы лояльности на основе суммы заказа и вызывает AddPoints для клиента.
2) Клиент может использовать баллы лояльности, когда их минимальное значение составляет 250.
Приведённый выше код идеально отвечает этим требованиям.

Допустим, теперь клиент может обновить существующий заказ. Когда товар удаляется из заказа, объект заказа должен рассчитать разницу в баллах лояльности и вычесть её из суммы клиента. Вот три возможных решения:
1) Повторно использовать RedeemPoints для вычитания. Но этот метод проверяет минимальное значение 250 и выдаст исключение для клиента без баллов лояльности.
2) Использовать AddPoints, передав отрицательное число. Но по бизнес-правилам LoyaltyPoints не может быть отрицательным.
3) Ввести отдельный метод, который не проверяет минимум в 250:
public void SubtractPoints(LoyaltyPoints pts)
=> Points -= points;
Но теперь у нас два публичных метода, которые выполняют вычитание, и неочевидно, какой когда использовать. Это является признаком того, что мы раскрываем детали реализации.

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

Вместо сохранения остатка, нужно хранить два исходных значения и вычислять остаток на лету:
public class Customer : Entity 
{
public LoyaltyPoints Earned { get; private set; }
public LoyaltyPoints Redeemed { get; private set; }

public LoyaltyPoints Points
=> Earned - Redeemed;

public void IncreasePoints(LoyaltyPoints pts)
=> Earned += pts;

public void ReducePoints(LoyaltyPoints pts)
=> Earned -= pts;

}

Одно поле не передавало должным образом значение метода SubtractPoints. Это может означать любой из двух вариантов использования:
- снятие заработанных баллов,
- добавление использованных баллов.

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

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

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

Источник:
https://enterprisecraftsmanship.com/posts/storing-information-in-its-highest-form/
👍19
День 1525. #ЗаметкиНаПолях
Храните Информацию в Её Высшей Форме. Окончание
Начало

Мы можем пойти дальше. Заработанные баллы – это сумма заработанных баллов по всем заказам. А использованные – сумма по всем использованиям. Поэтому мы можем хранить баллы лояльности, полученные/использованные в каждом заказе, в классе Order и список заказов в поле класса Customer:
public class Customer : Entity 
{
public Order[] Orders { get; private set; }

public LoyaltyPoints Earned
=> Orders.Sum(x => x.PointsEarned);

public LoyaltyPoints Redeemed
=> Orders.Sum(x => x.PointsRedeemed);

public LoyaltyPoints Points
=> Earned - Redeemed;

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

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

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

Каждый проект отличается и трудно сформулировать общее правило. Можно сказать, что следует хранить источники, пока влияние недостатков от их хранения минимально:
- Для продолжительности фильма нет никакой разницы между сохранением целого числа и строки, так что это не проблема.
- Для баллов лояльности два поля также мало чем отличаются от одного — они оба могут храниться в одной таблице базы данных.
- Сохранение баллов в классе Order может быть уместным, если количество заказов на клиента невелико, и можно сделать Заказ частью агрегата Клиента.
- А если количество заказов на одного клиента велико и нужно сделать Заказ агрегатом, то лучше остановился на решении с двумя полями Earned и Redeemed.

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

Источник: https://enterprisecraftsmanship.com/posts/storing-information-in-its-highest-form/
👍12
День 1526. #ЧтоНовенького
Иерархические Данные в Entity Framework Core 8 Превью 2
Пакет EntityFrameworkCore.SqlServer.HierarchyId — это неофициальный способ добавления использования иерархических данных в Entity Framework, который доступен уже несколько лет (версия 1.0.0 пакета доступна с апреля 2020 г.), однако в EF Core 8 preview 2 эта функция имеет официальную реализацию, основанную на этом пакете от сообщества. Новый официальный пакет — Microsoft.EntityFrameworkCore.SqlServer.HierarchyId.

HierarchyId включается путем установки вышеупомянутого пакета и следующего кода в startup приложения:
options.UseSqlServer(
connectionString,
x => x.UseHierarchyId());

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

В самом типе сущности этот тип используется как любой другой тип свойства:
public HierarchyId NodePath { get; set; }

Тип представляет путь сущности в древовидной структуре. Например, для узла с путем /1/2/3:
- / - корень дерева,
- 1 – прародитель узла,
- 2 - родитель узла,
- 3 — идентификатор узла.

Пути /1/2/4 и /1/2/5 являются родственными узлами исходного узла, а /1/3 и /1/4 являются родственными узлам родительского узла. Узлы также можно вставлять между двумя другими узлами, используя десятичные значения. Узел /1/3.5 находится между узлами /1/3 и /1/4.

Хотя этот формат удобочитаем в коде, сам SQL Server использует компактный двоичный формат для хранения этого идентификатора (varbinary).

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

Предварительные версии EF8 в настоящее время можно использовать в .NET 6 LTS и .NET 7. Выпуск EF8 согласован со следующей LTS-версией .NET 8, которая запланирована на ноябрь 2023 года.

Источник: https://www.infoq.com/news/2023/04/ef-core-8-preview-2/
👍8
День 1527. #TipsAndTricks #Git
Некоторые Малоизвестные Приемы в Git
Сегодня несколько полезных советов при работе с Git для любителей консоли.

1. Изменение последнего коммита
Когда вы делаете коммит в репозитории Git, вы создаёте новый снимок своего кода. Иногда вы можете обнаружить, что забыли включить файл, допустили опечатку в сообщении коммита или внесли другие небольшие изменения, которые хотели бы включить в последний коммит. Git позволяет изменить последний коммит с помощью флага --amend:
# изменить последний коммит новым сообщением
git commit --amend -m "New message"

# изменить, не меняя сообщения
git commit --amend --no-edit

2. Reflog
Git отслеживает все изменения, которые вы вносите в репозиторий, включая коммиты, слияния и другие операции. Reflog — это журнал всех изменений в репозитории Git, включая все коммиты, изменения веток и другие операции. Вы можете использовать журнал ссылок для восстановления потерянных коммитов, возврата к предыдущему состоянию или отмены перебазирования.
Чтобы просмотреть журнал ссылок, запустите:
git reflog

# возврат к предыдущему состоянию
git reset HEAD@{N}

3. Интерактивное перебазирование
Это мощный инструмент, который позволяет редактировать, изменять порядок или удалять коммиты перед их слиянием с основной веткой. Это особенно полезно, когда вы работаете над функциональной веткой и хотите очистить историю коммитов перед её слиянием с основной веткой:
git rebase -i HEAD~N

# изменить сообщение коммита
pick 1234567 Old message
reword 2345678 New message

# изменить порядок коммитов
pick 1234567 First commit
pick 2345678 Second commit
pick 3456789 Third commit

# удалить коммит
pick 1234567 First commit
drop 2345678 Second commit
pick 3456789 Third commit

4. Git-псевдонимы
Псевдонимы Git позволяют создавать собственные ярлыки для команд Git. Это может сэкономить ваше время и объём ввода, особенно для часто используемых команд:
git config --global alias.ci commit

# использование
git ci -m "Commit message"

5. Git Stash
Git stash позволяет временно сохранять изменения, которые вы ещё не готовы зафиксировать, без создания новой ветки:
git stash

# применить последние сохранённые изменения
git stash apply

# применить выбранные сохранённые изменения
git stash apply stash@{N}

# список всех изменений
git stash list

Больше консольных команд Git в этом посте.

Источник: https://dev.to/atordvairn/some-secret-git-tricks-that-come-in-handy-2k8i
👍17
День 1528. #Карьера
Решение Проблем — Это не Навык

Самое главное, когда дело касается программирования, это конкретные знания, необходимые для решения задачи.

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

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

Но многие люди, кажется, повторяют, что программирование — это просто решение проблем, потому что легче сказать это, чем дать конкретный совет. Только когда я наткнулся на этот пост Скотта Янга «Cognitive Load Theory», я понял, почему идея о том, что решение проблем является основным мета-навыком программирования, казалась мне подозрительной.

Решение проблем — это не навык. Способ, которым мы учимся решать проблемы, заключается в наличии (а) знаний, которые помогают решить проблему, и (б) автоматических процедурных компонентов, которые помогают в решении проблем. Вероятно, не существует общих методов решения проблем, которые работали бы для каждой области. Могут существовать эвристики для решения проблем внутри предметной области. Тем не менее, их значение несравнимо со способностью иметь в памяти тонны выученных паттернов. Это объясняет, почему передача знаний ненадёжна и почему опыт имеет тенденцию быть конкретным для области применения.

Другими словами, чтобы решить задачу в программировании, вам нужно иметь определённые знания в области, позволяющие её решить. Конечно, Шерлок Холмс с его способностью к дедукции и логическому мышлению мог бы придумать что-то близкое к псевдокоду для решения FizzBuzz, но, если он не знает, как писать на языке программирования, он не сможет действительно решить задачу. Ему нужно знать разницу между чётными и нечётными числами (я уверен, что знает), а также специфический синтаксис языка программирования, например операторы if и что делает оператор деления по модулю %.

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

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

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

Источник: https://betterprogramming.pub/problem-solving-isnt-a-skill-a32c22b71602
Автор оригинала: Jay Cruz
👍11👎4
День 1529. #TipsAndTricks
9 Советов по Fluent Assertions, Которые Сэкономят Вам Время. Начало
Хороший программист всегда думает о будущем ПО, о том, как написать код, который будет легко читать и понимать.

Один из лучших способов улучшить читаемость юнит-тестов — использовать Fluent Assertions https://fluentassertions.com/ — набор методов расширения для утверждений, делающий их более читаемыми и понятными. Fluent API означает, что библиотека полагается на цепочку методов. Вы комбинируете несколько методов в одном операторе без необходимости сохранять промежуточные результаты в переменных. Вот несколько примеров.

1. Идентификация субъекта – Be()
Первый пример простой. Мы хотим проверить, равно ли целое число 5:
int number = 5;
number.Should().Be(5);

Можно добавить сообщение. Отличительной особенностью сообщений Fluent Assertions, является то, что вы добавляете объяснение в середину сообщения:
int number = 6;
number.Should().Be(5,
"because that is the correct amount");

В итоге получается следующее сообщение:
Expected number to be 5 because that is the correct amount, but found 6. (Ожидалось, что number будет 5, потому что это правильное количество, но найдено 6.)

2. Основные утверждения
Для всех ссылочных типов:
sut.Should().BeNull();
….NotBeNull();
….BeOfType<Customer>();
// для сравнения используется Equals
….Be(otherCustomer);

Для строк:
"string".Should().BeNullOrEmpty();
….BeNullOrWhiteSpace();
….NotBeNullOrEmpty();
….NotBeNullOrWhiteSpace();

Для логических типов доступны BeTrue и BeFalse.

Для числовых типов:
number.Should().Be(1);
….NotBe(10);
….BePositive();
….BeNegative();
….BeGreaterThanOrEqualTo(88);
….BeGreaterThan(66);
….BeLessThanOrEqualTo(56);
….BeLessThan(61);
….BeInRange(1, 5);
….NotBeInRange(6, 9);

3. BeEquivalentTo — сравнение графов объектов
BeEquivalentTo — позволяет сравнить, имеют ли два объекта одинаковые свойства с одинаковыми значениями. Два объекта не обязательно должны быть одного типа. Вот примечание к этому методу от авторов:
Объекты эквивалентны, когда оба графа объектов имеют свойства с одинаковыми именами и одинаковыми значениями, независимо от типа этих объектов. Два свойства также равны, если один тип может быть преобразован в другой, и результат равен. Тип свойства коллекции игнорируется до тех пор, пока коллекция реализует System.Collections.Generic.IEnumerable’1 и все элементы коллекции структурно одинаковы. Обратите внимание, что фактическое поведение определяется глобальными значениями по умолчанию, управляемыми FluentAssertions.AssertionOptions.

Довольно часто встречаются классы с одинаковыми свойствами. Это особенно актуально, когда методы API обычно принимают DTO в качестве параметра:
customer.Should()
.BeEquivalentTo(customerDto);

Разница между Be и BeEquivalentTo в том, что Be сравнивает два объекта на основе реализации System.Object.Equals(System.Object). BeEquivalentTo сравнивает свойства и требует, чтобы свойства имели одинаковые имена, независимо от фактического типа свойств.

4. Цепочки утверждений - And
Чтобы связать несколько утверждений, вы можете использовать ограничение And. Пример:
collectionToTest.Should().Contain("first")
.And.HaveElementAt(2, "third");

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

Источник: https://methodpoet.com/fluent-assertions/
👍17
День 1530. #TipsAndTricks
9 Советов по Fluent Assertions, Которые Сэкономят Вам Время. Продолжение
Начало

5. Методы расширения коллекции
var coll = new List<string>
{ "first", "second" };
var coll2 = new List<string>
{ "first", "second" };

Методов расширения для коллекций множество. Думаю, имена говорят сами за себя:
coll.Should().NotBeEmpty();
….HaveCount(2);
….Equal(coll2);
….Equal("first", "second");
….BeEquivalentTo(coll2);
….NotBeEquivalentTo(
new List<string> { "1", "2" });

….OnlyHaveUniqueItems();
….HaveCountGreaterThan(1);
….HaveCountGreaterThanOrEqualTo(2);
….HaveCountLessThanOrEqualTo(5);
….HaveCountLessThan(5);
….NotHaveCount(1);

….StartWith("first");
….StartWith(
new List<string> { "first" });
….EndWith("second");
….EndWith(
new List<string> { "first", "second" });
….Contain("first")
.And.HaveElementAt(1, "first");

….BeEmpty();
….BeNullOrEmpty();
….NotBeNullOrEmpty();
….ContainInOrder(
new List<string> { "first", "second" });
….NotContainInOrder(
new List<string> { "1", "2", "3" });

….BeInAscendingOrder();
….BeInDescendingOrder();
….NotBeInAscendingOrder();
….NotBeInDescendingOrder();

6. Утверждения даты и времени
DateTime date = new DateTime(2022, 2, 7);

date.Should().Be(7.February(2022).At(0,0));
….BeAfter(6.February(2022).At(23,59));
….BeBefore(8.February(2022).At(0,1));
….BeSameDateAs(7.February(2022));

….NotBe(6.February(2022));
….NotBeAfter(8.February(2022).At(10, 28));
….NotBeBefore(1.January(2022));
….NotBeSameDateAs(27.February(2022));

7. Область действия утверждений - AssertionScope
Вы можете использовать AssertionScope для объединения нескольких утверждений в одно исключение. Это делает код чище и позволяет проверить сразу все утверждения, вместо завершения теста после первой неудачи:
int numberOfDocuments = 5;
using (new AssertionScope())
{
numberOfDocuments.Should().Be(6);
"First Name".Should().Be("Last Name");
}

В примере выше будут отображены оба сбоя и возникнет исключение в момент удаления AssertionScope:
Expected numberOfDocuments to be 6, but found 5.
Expected string to be "Last Name" with a length of 9, but "First Name" has a length of 10, differs near "Fir" (index 0).
(Ожидалось, что numberOfDocuments будет равно 6, но найдено 5.
Ожидалось, что строка будет «Last Name» длиной 9, но «First Name» имеет длину 10 и отличается рядом с «Fir» (индекс 0)
).

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

Источник:
https://methodpoet.com/fluent-assertions/
👍10
День 1531. #TipsAndTricks
9 Советов по Fluent Assertions, Которые Сэкономят Вам Время. Окончание
Начало
Продолжение

8. Проверка исключений
Теперь проверим, что метод выдаёт исключение:
Action act = () => sut.BadMethod();
act.Should().Throw<ArgumentException>();

И наоборот:
Action act = () => sut.GoodMethod();
act.Should()
.NotThrow<NullReferenceException>();

Также поддерживаются асинхронные версии:
Func<Task> act = () => sut.iss.onethodAsync();

await act.Should()
.ThrowAsync<ArgumentNullException>();

await act.Should().NotThrowAsync();

9. Создание утверждений
Вы можете написать свои утверждения, которые проверяют ваши классы и терпят неудачу, если условие не выполняется. Следующее утверждение ищет символ @ в поле адреса электронной почты. Переменная email представляет собой строку, поэтому создадим метод расширения для StringAssertions:
public static class EmailStringExtensions
{
public static AndConstraint<StringAssertions>
ContainAtSign(
this StringAssertions email,
string because = "",
params object[] becauseArgs)
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(email.Subject.Contains('@'))
.FailWith(
"Expected email to contain @{reason}.");

return new AndConstraint<StringAssertions>(email);
}
}

Использование:
string email = "[email protected]";
email.Should()
.ContainAtSign("because that’s valid email");

Также можно создавать утверждения для ваших классов, наследуя от ReferenceTypeAssertions.

Итого
Недостаточно знать, как писать юнит-тесты, необходимо писать читаемые тесты. Один из лучших способов — использовать Fluent Assertions. Эта библиотека позволяет писать чётко определённые утверждения, которые позволяют любому, кто читает ваши тесты, понять, что именно они тестируют. Я представил лишь небольшую часть функциональных возможностей Fluent Assertions, их гораздо больше (см. документацию).

Источник: https://methodpoet.com/fluent-assertions/
👍19
День 1532. #ЧтоНовенького
Улучшения Аутентификации и Идентификации в
ASP.NET Core 8
Команда ASP.NET Core совершенствует проверку подлинности, авторизацию и управление идентификацией в .NET 8.

Для аутентификации в ASP.NET Core сейчас есть несколько вариантов:
1. ASP.NET Core Identity.
2. Azure Active Directory (Azure AD).
3. Различные сторонние решения в виде пакетов, контейнеров и облачных сервисов.

Сегодня существуют ограничения на использование ASP.NET Core Identity в SPA-приложениях. Традиционный способ настройки страниц, связанных с идентификацией, заставляет приложение вернуться к серверным веб-страницам, а также использовать внешние (сторонние) пакеты для поддержки аутентификации на основе токенов.

Поэтому планируется устранить зависимость от Duende IdentityServer из шаблонов SPA в .NET 8. IdentityServer остается отличным вариантом для self-hosting сценариев и остаётся бесплатным, если вы соответствуете требованиям community edition. Но существует множество других вариантов, включая OpenIddict или Keycloak. Теперь шаблон приложения будет содержать ссылку на страницу документации с пояснением вариантов реализации аутентификации. Кстати, про Keycloack вчера был доклад на Ozon Tech Meetup.

Многим не нужны сложности OAuth/OpenID Connect, а просто нужна возможность проверки личности пользователя через вход в систему и доступ к ресурсам на основе разрешений. Эта поддержка встроена в платформу ASP.NET Core Identity с момента её выпуска. Она обеспечивает стандартную аутентификацию на основе файлов cookie.

Вот области, которые планируется улучшить в ASP.NET Core 8:
1. Расширить существующую аутентификацию на основе cookie для поддержки настройки в приложениях SPA. Сейчас, чтобы настроить вход в систему, нужно переопределить серверные страницы Identity. Это приводит к непоследовательному взаимодействию с клиентами: переходу от одностраничного веб-приложения к серверному. Команда добавит конечные точки API, которые позволят разработчикам «оставаться внутри SPA».
2. Добавить поддержку аутентификации на основе токенов. Хотя существующее решение на основе cookie работает, решения для аутентификации на основе токенов в наши дни стали гораздо более гибкими. Аутентификация на основе токенов в основном будет соответствовать возможностям и функциональности cookie-решения, но инкапсулировать данные аутентификации в токен, что позволит работать в сценариях, где файлы cookie неуместны или неоптимальны.

Кроме того, для .NET 8 планируется переработка документации: создание единой точки для изучения доступных вариантов, .NET Auth, которая объединит ссылки на учебные пособия и примеры и будет содержать конкретные рекомендации. Например, SPA без внешних зависимостей предъявляет требования к аутентификации отличные от бизнес-приложения с серверной частью, базой данных, сторонними API-зависимостями и входом через социальные сети.

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

Источник: https://devblogs.microsoft.com/dotnet/improvements-auth-identity-aspnetcore-8/
👍19
День 1533. #Курсы
.NET Day
Сегодня пройдёт онлайн митап .NET Day. Желающие узнать что-то новое, регистрируйтесь здесь. А вот программа митапа.

11:00 – 11:45 (Мск.)
Райнер Стропек «Изучение новейших возможностей .NET и C# через создание игры»
Быть в курсе событий важно для разработчиков ПО, но это не должно быть скучно. На этом занятии Райнер Стропек продемонстрирует интересные возможности последних выпусков .NET и C#, создав небольшую игру на основе Skia. Вы узнаете о новостях, связанных с десериализацией JSON, сопоставлением по шаблону, записями, file-scoped типами и многими другими новыми функциями языка. На этом сеансе будет только код, без слайдов. Райнер предполагает, что у вас уже есть хорошие навыки работы с C# и .NET, и вы хотели бы узнать о последних изменениях в платформе.

12:00 – 12:45 (Мск.)
Флориан Раппл «Микрофронтенды с Blazor»
Микрофронтенды стали полезным инструментом для разбиения пользовательского интерфейса на более мелкие фрагменты, которые могут разрабатываться и развёртываться независимыми командами. В настоящее время приложения Blazor по-прежнему разрабатываются в основном в виде монолита. Хотя библиотеки и компоненты могут разрабатываться независимо, их развёртывание по-прежнему осуществляется централизованно. В долгосрочной перспективе это становится узким местом. В этом докладе эксперт по микрофронтендам Флориан Раппл представляет устоявшуюся архитектуру для создания модульных фронтенд-приложений. Он покажет, как эту архитектуру можно реализовать на Blazor для создания динамичного взаимодействия с пользователем.

13:00 – 13:45 (Мск.)
Денни Деклерк «Основы специальных возможностей»
Вы хотите научиться делать доступные веб-сайты для всех людей в мире, включая миллиард людей с ограниченными возможностями, с помощью Blazor? Тогда этот доклад определённо стоит посмотреть. Денни Деклерк расскажет, как создавать интерфейсы PWA и веб-сайтов с помощью Blazor, как сделать веб-сайты, соответствующие WCAG, действительными для всех известных групп инвалидов. Помимо Blazor, вы познакомитесь с основами семантического HTML и ARIA, узнаете о важности ALT-текста для изображений, использовании цвета, веб-сайтах доступных для программ чтения с экрана, а также о веб-сайтах, которые должны быть понятными и не сбивать пользователей с толку.

14:00 – 14:45 (Мск.)
Саймон Пейнтер «По Орегонскому пути с функциональным C#»
В 1971 году трое студентов подумали, что могут оживить лекцию по истории, создав компьютерную игру, в которую студенты могли бы играть, и после нескольких дней работы на HP Time Share BASIC они придумали то, что оказалось важной вехой в истории компьютерных игр - Oregon Trail. Однако в этом докладе акцент будет сделан не столько на исторических компьютерных играх, сколько на .NET и функциональном программировании. Задача, которую автор доклада поставил перед собой, состоит в том, чтобы переработать Oregon Trail на C#, используя следующие ограничения:
- Почти 100% покрытие модульными тестами,
- Никакие переменные не могут менять состояние после его установки,
- Никаких операторов (for, foreach, if, where и т. д.), если только их буквально невозможно избежать.
Кроме того, Саймон продемонстрирует несколько приёмов, которые может предложить функциональное программирование, например функции высшего порядка, функциональные потоки с простыми монадами и хвостовую рекурсию.

Кстати, об этом и о других предстоящих событиях я узнал из календаря предстоящих мероприятий, любезно предоставленного PVS-Studio.
👍5
День 1534. #ЧтоНовенького
DotNetIsolator
Вам когда-нибудь требовалась изолированная песочница для запуска кода? Мне тоже нет, но вдруг когда-нибудь пригодится. Сегодня порекомендую вам видео от разработчика Microsoft Стива Сандерсона. Он описывает новый экспериментальный пакет для создания такой изолированной среды – DotNetIsolator.

Пакет позволяет вашему коду .NET легко запускать другой код .NET в изолированной среде. Вы можете создать столько сред, сколько хотите. Каждая из них на самом деле является песочницей WebAssembly. Дело в том, что внутри WebAssembly мы можем исполнять код .NET. Но и сам .NET может создавать и управлять средой WebAssembly. DotNetIsolator соединяет две эти концепции.

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

Подробное объяснение с несколькими примерами в видео https://youtu.be/5u1UaqkPZbg

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

В общем, для производственного кода он явно не подходит, но поиграться, кому интересно, можно.
День 1535. #ЗаметкиНаПолях
LINQ на Стероидах с SIMD
В этом посте рассмотрим использование SIMD-инструкций для ускорения запросов LINQ и посмотрим, как это работает совместно с «обобщённой математикой» в C# 10.

Допустим, у нас есть список чисел, и мы хотим найти сумму. Самый быстрый способ — использовать SIMD-инструкции (Single Instruction Multiple Data), которые позволяют выполнять одну и ту же операцию с несколькими значениями данных одновременно. Внимание: есть две ловушки, связанные с этим:
- Это гораздо сложнее, чем простой цикл или использование LINQ.
- Не рекомендуется для небольших наборов данных и когда производительность не критична.

Точкой входа в SIMD-операции является тип Vector<T>. T может быть любого числового типа. Тип Vector<T> имеет свойство Count, которое сообщает, сколько элементов он может содержать. Это важно, потому что, если мы, например, складываем два вектора друг с другом, мы делаем это за "одну" операцию. Результатом будет вектор с тем же количеством элементов, что и входные векторы. Чем больше элементов мы можем хранить, тем быстрее будет работать код. Это зависит от вашей архитектуры. В вашем процессоре есть SSE: «Streaming SIMD Extensions» - набор инструкций, позволяющих выполнять SIMD-операции. Размер векторов зависит от версии SSE, которую поддерживает ЦП: SSE2 – 2 элемента, SSE4.1 – 4 и так далее.

С появлением C# 10 и «обобщённой математики», мы можем использовать операции SIMD для любого числового типа, не создавая функцию для каждого типа в отдельности.

Идея состоит в том, что мы разбиваем нашу проблему на более мелкие подзадачи, которые будут решаться за одну операцию. Например, функция Min: разбиваем весь массив на мелкие фрагменты и ищем в каждом наименьшее значение.
public static T Min<T>(this Span<T> span)
where T : unmanaged, IMinMaxValue<T>, INumber<T>

Для простоты мы используем Span<T>. Нам нужна непрерывная память, так как блоки памяти передаются в процессор SIMD, который не может обрабатывать произвольные адреса памяти.
- INumber<T> — означает, что тип является числом.
- IMinMaxValue<T> — тип имеет минимальное и максимальное значение.
- unmanaged - тип является типом значения и не содержит ссылочных типов внутри себя. Таким мы можем использовать stackalloc, и запрещать сценарии, в которых структура со ссылочным типом внутри даёт неожиданные результаты.

Сначала создаём вектор с максимальным значением типа и приводим Span непосредственно к вектору:
var spanAsVectors = 
MemoryMarshal.Cast<T, Vector<T>>(span);
Span<T> vector =
stackalloc T[Vector<T>.Count];
vector.Fill(T.MaxValue);
var min = new Vector<T>(vector);

Далее сравниваем каждую запись вектора с каждым другим вектором, чтобы получить минимум:
foreach (var v in spanAsVectors)
min = Vector.Min(v, min);

В итоге получим вектор с n элементами, один из которых является минимальным. НО! MemoryMarshal.Cast<T, Vector<T>>(span) имеет большой недостаток: если у нас Vector<T>.Count = 4, а всего элементов 9, функция вернёт только 2 вектора. Поэтому мы должны выделить и сравнить «остаток»:
var remain = span.Length % Vector<T>.Count;
if (remain > 0)
{
Span<T> last =
stackalloc T[Vector<T>.Count];
last.Fill(T.MaxValue);
span[^remain..].CopyTo(last);
min = Vector.Min(min, new Vector<T>(last));
}

Теперь получим минимальное значение из вектора:
var minVal = T.MaxValue;
for (var i = 0; i < Vector<T>.Count; i++)
minVal = T.Min(minVal, min[i]);

return minVal;

Результаты бенчмарка для 100000 случайных чисел float впечатляют:
Method     Mean    Ratio
Linq 67.41us 1.00
For 55.58us 0.82
LinqSIMD 10.78us 0.16

С появлением обобщённой математики мы теперь можем выполнять SIMD-операции для пользовательских типов и для более широкого диапазона типов без использования дополнительного кода. Это огромный шаг вперёд для экосистемы .NET.
Код этого примера, а также других Linq функций можно найти в библиотеке LinqSIMDExtensions, которая также доступна в виде пакета nuget.

Источник: https://steven-giesel.com/blogPost/faf06188-bae9-484d-804d-a42d58d18cad
👍16
День 1536.
Сегодня порекомендую вам вебинар от JetBrains «Объектно-Ориентированное и Функциональное Программирование на C# и F#» https://youtu.be/vOcTPUiBQgc.

Урс Энцлер, архитектор ПО в Calitime AG с более чем 20-летним опытом в C#, рассказывает о сходствах и различиях между функциональным и объектно-ориентированным программированием на примерах C# и F#.

Современные языки программирования в значительной степени поддерживают оба подхода. В чем разница между ними, какие понятия используются, как мыслить в терминах ООП и ФП и где один более уместен, чем другой? Можно ли объединить эти две парадигмы программирования? Где в проектах обычных бизнес-приложений можно применять ООП, а где функциональное программирование? Какие функции каждого подхода поддерживаются в C# и F#?

PS: после доклада в секции Q&A Урс отвечает, как переписать систему с использованием F#, как перейти на использование F#, изучить синтаксис и т.п.
👍8
День 1537. #ЧтоНовенького
Новые Функции C#12. Начало
В недавно выпущенном превью 3 .NET 8 добавили несколько интересных новых функций C#.

1. Первичные конструкторы для классов и структур
Первичные конструкторы были введены для записей в C#9. C#12 распространяет их на все классы и структуры:
public class Student
(int id, string name, IEnumerable<decimal> grades)
{
public Student(int id, string name)
: this(id, name, Enumerable.Empty<decimal>()) { }

public int Id => id;
public string Name { get; set; }
= name.Trim();
public decimal GPA =>
grades.Any() ? grades.Average() : 4.0m;
}

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

Когда параметры первичного конструктора используются в методах или средствах доступа к свойствам (параметр grades в классе Student выше), их необходимо захватить, чтобы они оставались доступными после завершения выполнения конструктора. Это похоже на то, как параметры и локальные переменные захватываются в лямбда-выражениях. Для параметров первичного конструктора захват реализуется путём создания приватного резервного поля в классе или структуре. Поле имеет «непроизносимое» имя, что означает, что оно не будет конфликтовать с другими именами и не будет очевидно при использовании рефлексии. Подумайте, как назначать и использовать параметры первичного конструктора, чтобы избежать двойного хранения данных. Например, name используется для инициализации автоматического свойства Name, которое имеет собственное резервное поле. Если бы другой член напрямую ссылался на параметр name, оно также сохранялось бы в собственном резервном поле, что приводило к ненужному дублированию.

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

Вы можете оставить отзыв о первичных конструкторах на GitHub в обсуждении Preview Feedback: C# 12 Primary constructors.

Чтобы попробовать новые функции, загрузите последнюю превью версию Visual Studio 17.6 или превью 3 .NET 8.

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

Источник:
https://devblogs.microsoft.com/dotnet/check-out-csharp-12-preview/
👍11
День 1538. #ЧтоНовенького
Новые Функции C#12. Окончание
В недавно выпущенном превью 3 .NET 8 добавили несколько интересных новых функций C#.
Начало. Первичные конструкторы

2. Директивы using для дополнительных типов
Добавлен ещё один вариант использования ключевого слова using. Теперь с помощью него можно объявить дополнительный тип:
using Measurement = (string, int);
using PathOfPoints = int[];
using DatabaseInt = int?;

Вы можете использовать псевдоним практически для любого типа. Можно создавать псевдонимы для обнуляемых типов-значений, однако нельзя использовать псевдонимы для обнуляемых ссылочных типов. Кортежи особенно интересны, потому что вы можете включать имена и типы элементов:
using Measurement = (string Units, int Distance);

Псевдонимы можно использовать так же, как любые типы:
public void F(Measurement x)
{ }

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

Вы можете оставить отзыв о первичных конструкторах на GitHub в обсуждении Preview Feedback: C# 12 Alias any type.

3. Значения по умолчанию для лямбда-выражений
Синтаксис такой же, как и для других параметров по умолчанию:
var addWithDefault = 
(int addTo = 2) => addTo + 1;
addWithDefault(); // 3
addWithDefault(5); // 6

Значение по умолчанию будет передано в метаданные и доступно через рефлексию как DefaultValue свойства ParameterInfo лямбда-метода:
addWithDefault.iss.onethod
.GetParameters()[0].DefaultValue; // 2

До C#12 нужно было использовать локальную функцию или громоздкое DefaultParameterValue из пространства имен System.Runtime.InteropServices, чтобы указать значение по умолчанию для параметров лямбда-выражения. Эти подходы по-прежнему работают, но их сложнее читать, и они несовместимы со значениями по умолчанию для методов. Теперь будет согласованный поиск значений параметров по умолчанию для методов, конструкторов и лямбда-выражений.

Вы можете оставить отзыв о первичных конструкторах на GitHub в обсуждении Preview Feedback: C# 12 Default values in lambda expressions.

Чтобы попробовать эти новинки, загрузите последнюю превью версию Visual Studio 17.6 или превью 3 .NET 8.

Источник: https://devblogs.microsoft.com/dotnet/check-out-csharp-12-preview/
👍16
День 1539. #ЗаметкиНаПолях
Чистые SQL-запросы в EF Core
В EF7 появилась поддержка возврата скалярных типов с помощью SQL-запросов. В EF8 появилась поддержка запросов несопоставленных (unmapped) типов с помощью чистого SQL. Это то, что Dapper предлагает по умолчанию, и приятно видеть, что EF Core догоняет его.

EF Core и SQL
В EF8 чистые SQL-запросы могут возвращать любой тип без необходимости включать его в модель EF. Вы можете запрашивать несопоставленные типы с помощью методов SqlQuery и SqlQueryRaw. Метод SqlQuery использует интерполяцию строк для параметризации запроса, защищая от атак SQL-инъекцией.

Вот пример запроса, возвращающего список OrderSummary:
var start = new DateOnly(2023, 1, 1);
var orders = await dbContext
.Database
.SqlQuery<Order>(
@$"SELECT * FROM Orders
AS o WHERE o.Created >= {start}")
.ToListAsync();

В БД будет отправлен следующий запрос:
SELECT * FROM Orders AS o WHERE o.Created >= @p0

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

SQL + LINQ
Интересной особенностью SqlQuery является то, что он возвращает IQueryable, который можно дополнить с помощью LINQ. Можно добавить оператор Where после вызова SqlQuery:
var orders = await dbContext
.Database
.SqlQuery<Order>(
"SELECT * FROM Orders AS o")
.Where(o => o.Created >= start)
.ToListAsync();
Однако, в результате получится неоптимальный запрос:
SELECT s.Id, s.CustomerId, s.Price, s.Created
FROM (
SELECT * FROM Orders AS o
) AS s
WHERE s.Created >= @p0

Также возможно добавить OrderBy, Skip и Take:
var orders = await dbContext
.Database
.SqlQuery<Order>(
@$"SELECT * FROM Orders AS o
WHERE o.Created >= {start}")
.OrderBy(o => o.Id)
.Skip(10)
.Take(5)
.ToListAsync();

Получим следующий запрос:
SELECT s.Id, s.CustomerId, s.Price, s.Created
FROM (
SELECT * FROM Orders AS o WHERE o.Created >= @p0
) AS s
ORDER BY s.Id
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY

Производительность аналогична LINQ-запросам с проекцией с помощью Select.

SQL-запросы для модификации данных
Если вы хотите изменить данные в базе данных с помощью SQL, вы обычно пишете запрос, который не возвращает результата. Запрос может быть UPDATE, DELETE или вызовом хранимой процедуры. Для этого можно использовать метод ExecuteSql:
dbContext.Database.ExecuteSql(
@$"UPDATE Orders SET Status = 5
WHERE Created >= {start}");

ExecuteSql также защищает от SQL-инъекций путем параметризации аргументов, как и SqlQuery. В EF7 можно написать приведённый выше запрос с помощью LINQ и метода ExecuteUpdate. Также есть метод ExecuteDelete для удаления записей.

Источник: https://www.milanjovanovic.tech/blog/ef-core-raw-sql-queries
👍15
День 1540. #ЧтоНовенького
GitHub Представил Правила Репозитория
Правила для репозиториев — это следующая ступень эволюции защиты веток GitHub, которая поможет сделать репозитории более безопасными и совместимыми.

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

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

Страница набора правил — это центральное место для просмотра и управления всеми правилами репозитория. Она показывает действующие правила и позволяет добавлять новые наборы правил или редактировать существующие.

При создании набора правил вы определяете его статус: активный (active) или отключенный (disabled). Для слияния коммита, все активные наборы правил должны проходить, в то время как отключенные наборы правил не применяются. Отключенные наборы не будут препятствовать слияниям, но позволят администраторам создавать правила перед их применением. Клиентам Enterprise Cloud также доступен статус «оценка» (evaluate) для наборов правил: режим «пробного запуска» для понимания влияния новых правил до того, как они будут активны и применены.

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

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

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

Кроме того, авторы ждут ваших отзывов в обсуждении.

Источник: https://github.blog/changelog/2023-04-17-introducing-repository-rules-public-beta/
👍5
День 1541. #МоиИнструменты
Обфускаторы в .NET
Распространенным подходом к защите интеллектуальной собственности является обфускация) символов и сокрытие кода IL. Веб-разработчики должны уделить особое внимание обфускации, поскольку код отправляется в браузер клиента.

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

1. Preemptive Dotfuscator
Microsoft продвигала Dotfuscator как обфускатор для .NET, но, скорее всего, они никогда не собирались его предоставлять как утилиту. Разработчики, переходящие с C++ и VB6 на .NET, не осознавали, что их код может быть раскрыт, и это считалось недостатком новой платформы.
Несмотря на то, что Preemptive Dotfuscator является функциональным продуктом, недавно они на 50% увеличили плату за подписку, что вряд ли обосновано.

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

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

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

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

Источник: https://blog.ndepend.com/in-the-jungle-of-net-obfuscator-tools/
👍14
День 1542. #Книги
«Высоконагруженные приложения. Программирование, масштабирование, поддержка» (Клеппман М. — СПб.: Питер, 2022).

Осилил «кабанчика»! Многие называют эту книгу одной из обязательных к прочтению для разработчика ПО.

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

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

Третья часть книги рассказывает о пакетной и потоковой обработке. Тут было что-то среднее, потому что с MapReduce, Hadoop, RabbitMQ и Kafka я не работал, но хотя бы что-то о них слышал.

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