.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
День 1915. #Оффтоп
Сегодня порекомендую очередное видео с ютуба, чтобы расслабиться после 6-дневной рабочей недели.
Мне всё больше и больше нравится этот чувак, ThePrimeTime)))

На этот раз он читает статью из 2016 года, где автор рассказывает о своей маме, программистке на COBOL для мейнфреймов. Рассказ об эпохе раннего программирования, где разработчики сталкивались с такими задачами, о которых нам не приходится и думать сейчас. Проблема в том, что до сих пор (!!!) есть крупные компании (например, банки), которые в своё время создали себе огромные системы на COBOL, и до сих пор поддерживают их за огромные деньги, потому что переписать это с нуля либо в принципе невозможно, либо стоит нереально дорого.

Мудрость дня: «То, что вы сейчас пишете либо будет выкинуто в течение полугода, либо останется жить на десятилетия.»

В общем, приятного просмотра https://youtu.be/qNytWcW38us.
👍6
День 1916. #ЧтоНовенького
Новый Формат Файлов Решений в Visual Studio
В превью версии Visual Studio 17.10 добавлен новый формат файла решения с расширением файла .slnx.

На протяжении десятилетий файл решения .sln основывался на собственном формате файла от Microsoft, многословном, трудночитаемом с кучей GUID. Вот пример:
# Visual Studio Version 17
VisualStudioVersion = 17.10.34814.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "App\App.csproj", "{F95781B3-A973-4D19-9585-974DA143E6A1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Application", "Application", "{6EC5A7A1-CA0C-42AC-BB97-19EF01B3C68C}"
EndProject

Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {85AD0F43-BB53-4378-94F7-8A7DF5A990D5}
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{06F979AA-0ABF-4B86-91D5-7E32B7F2A081} = {6C6714E6-D117-405B-9FFD-C4AF0643EA29}
{F95781B3-A973-4D19-9585-974DA143E6A1} = {6EC5A7A1-CA0C-42AC-BB97-19EF01B3C68C}
EndGlobalSection

EndGlobal

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

Оптимизированный формат файлов .slnx – отличное изменение:
<Solution>
<Folder Name="/Test/">
<Project Path="UnitTests\UnitTests.csproj" Type="Classic C#" />
</Folder>
<Folder Name="/Application/">
<Project Path="App\App.csproj" Type="Classic C#" />
</Folder>
</Solution>

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

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

Пока формат в предварительной версии и рекомендуется избегать его использования в производственной среде. Он всё ещё может подвергаться изменениям на основе отзывов сообщества.

Чтобы попробовать его в Visual Studio 2022 17.10 превью 3 или более поздней версии, в меню Tools > Options > Preview Features (Инструменты > Параметры > Предварительные функции) найдите Use Solution File Persistence Model. Затем в меню File > Save YourSolution.sln As… (Файл > Сохранить YourSolution.sln как…) выберите тип файла .slnx. Заметьте, что этот тип файлов пока не связан с Visual Studio в Windows, поэтому привязку придётся настроить вручную.

Источник: https://blog.ndepend.com/slnx-the-new-net-solution-xml-file-format/
👍24👎1
День 1917. #ЗаметкиНаПолях
Обмен Сообщениями с Помощью MassTransit

Создание распределённых приложений – дело непростое. Есть ряд потенциальных проблем, которые необходимо учитывать. Что делать, если в сети произошел сбой или сервис неожиданно прекратил работу? Способ взаимодействия частей распределённой системы становится критически важным.

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

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

Достоинства

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

Недостатки
1. Задержка: накладные расходы на обмен сообщениями добавляют дополнительную задержку.
2. Сложность: внедрение системы обмена сообщениями и управление дополнительной инфраструктурой могут повысить сложность проекта.

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

Например, у нас система обработки заказов и необходимо получить статус заказа из сервиса управления заказами. С помощью MassTransit создадим клиента, реализующего IRequestClient<T>, для инициирования запроса:
public class RequestController :
Controller
{
IRequestClient<StatusRequest> _client;


public async Task<IActionResult>
Get(string id, CancellationToken ct)
{
var response = await
_client.GetResponse<StatusResponse>(
new { id }, ct);

return Ok(response.Message);
}
}

Он отправит на шину сообщение GetOrderStatusRequest:
public record StatusRequest(string OrderId);

И будет ожидать ответа:
public record StatusResponse(
string OrderId,
short StatusCode,
string StatusText
}

В сервисе управления заказами ответчик (IConsumer<T>) будет прослушивать сообщения StatusRequest. Он получает запрос, возможно запрашивает БД и отправляет сообщение StatusResponse обратно на шину. Клиент будет ждать этого ответа и затем сможет обработать его соответствующим образом:
public class StatusRequestConsumer :
IConsumer<StatusRequest>
{
public async Task Consume(
ConsumeContext<StatusRequest> ctx)
{

await ctx
.RespondAsync<StatusResponse>(new
{
// задаём свойства ответа
});
}
}

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

Итого
Шаблон запрос-ответ — мощный инструмент при обмене сообщениями между сервисами. MassTransit значительно упрощает реализацию, гарантируя надежную доставку запросов и ответов.
Мы можем использовать запрос-ответ для реализации связи между модулями в модульном монолите. Однако не стоит доводить это до крайности, иначе система может страдать от увеличения задержки.

Источник: https://www.milanjovanovic.tech/blog/request-response-messaging-pattern-with-masstransit
👍10
День 1918. #Шпаргалка
Шпаргалка по Форматированию Дат

Структура System.DateTime, доступная только для чтения, определённая в библиотеке базовых классов .NET (BCL), является ключом к обработке дат и времени в C#. Отформатировать дату и время для вывода можно, указав формат в методе ToString("…"). Например:
csharp 
Console.WriteLine(
DateTime.Now.ToString("ddd, dd MMM yyyy h:mm"));

Выведет:
Wed 01 May 2024 8:01

Ниже приведены краткие пояснения некоторых часто используемых спецификаторов формата:
d – день месяца от 1 до 31.
dd – день месяца от 01 до 31.
ddd - аббревиатура дня недели (Mon, Tue, …).
dddd - полный день недели (Monday, Tuesday, …).
h - 12-часовой формат часов.
hh - 12-часовой формат часов с ведущим 0.
H - 24-часовой формат часов.
HH - 24-часовой формат часов с ведущим 0.
m - минуты.
mm - минуты с ведущим 0.
M - номер месяца.
MM - номер месяца с ведущим 0.
MMM - краткое имя месяца (Dec).
MMMM - полное имя месяца (December).
s - секунды.
ss - секунды с ведущим 0.
f - десятые доли секунды.
ff - сотые доли секунды.
t - краткое AM/PM (A или P).
tt - AM/PM (AM или PM).
yyyy - год.
z - часов от UTC (+3).
zz - часов от UTC с ведущим 0 (+03).
zzz - часов и минут от UTC (+03:00).
K - аналогично zzz, но адаптирует вывод в зависимости от типа времени (местное, UTC или не указано), поэтому может не вывести ничего.

Символы FHKMdfghmstyz%:/\"' зарезервированы в строке формата даты и времени. Они интерпретируются как символы форматирования, за исключением случаев, когда используются внутри одинарных или двойных кавычек, либо экранированы \. Все остальные символы последовательно интерпретируются как символьные литералы и остаются неизменными при выводе во время операции форматирования.

Также есть несколько стандартных спецификаторов формата, каждый из которых представлен одной буквой
* - зависит от культуры, ниже примеры для культуры "en-US":
"d" - короткая дата* (05/01/2024)
"D" - полная дата* (Wednesday, May 01, 2024)
"f" - полная дата, короткое время* (Wednesday, May 01, 2024 8:01 AM)
"F" - полная дата, полное время* (Wednesday, May 01, 2024 8:01:03 AM)
"g" - короткая дата, короткое время* (05/01/2024 8:01 AM)
"G" - короткая дата, полное время* (05/01/2024 8:01:03 AM)
"m" или "M" - день/месяц* (May 1)
"o" или "O" - полные дата/время по ISO 8601 (2024-05-01T08:01:03.7300000+04:00)
"s" - сортируемые дата/время по ISO 8601 (2024-05-01T08:03:01)
"r" или "R" - дата/время по RFC1123 (Wed, 01 May 2024 08:01:03 GMT)
"t" - короткое время* (8:01 AM)
"T" - длинное время* (8:01:03 AM)
"u" - универсальный сортируемый формат даты/времени (2024-05-01 05:01:03Z)
"U" - универсальный полный формат даты/времени (Wednesday, May 01, 2024 5:01:03 AM)
"y" или "Y" - месяц/год* (May 2024)
Любой другой символ приведёт к исключению FormatException во время выполнения.

Источник: https://blog.ndepend.com/csharp-datetime-format/
👍34
День 1919. #ЧтоНовенького
Вышла Бета-Версия pl/dotnet

Пакет pl/dotnet добавляет в PostgreSQL полную поддержку C# и F#.

В первой открытой для публики бета-версии (версия 0.99) поддерживаются все операции PL: функции, процедуры, DO, SPI, триггеры, записи, SRF, OUT/INOUT, табличные функции и т. д. Также поддерживаются 40 из 46 стандартных пользовательских типов. Пакет полностью совместим с NPGSQL, а SPI предоставляется через API NPGSQL для максимальной совместимости. Это 100% бесплатное ПО под лицензией PostgreSQL.

pl/dotnet предоставляет вам всю мощь C# и F# в процедурах, функциях и триггерах PostgreSQL. Например:
C#:
CREATE OR REPLACE FUNCTION dynamic_record_generator_srf(lim INT8)  
RETURNS SETOF record
AS $$
upperLimit = lim.HasValue ? lim : System.Int32.MaxValue;
for(long i = 0; i < upperLimit; i++)
{
yield return new object?[] {
i, $"Number is {i}" };
}
$$ LANGUAGE plcsharp;

F#
CREATE OR REPLACE FUNCTION dynamic_record_generator_srf_fsharp(lim INT8)  
RETURNS SETOF record
AS $$
let upperLimit = Option.defaultValue (int64 System.Int32.MaxValue) lim
seq { for i in 0L .. upperLimit - 1L do yield [| box i; $"Number is {i}" |] }
$$ LANGUAGE plfsharp;


Поддержка функций
Поддерживаются все типы функций SQL:
- обычные процедуры и функции;
- полная поддержка функций триггера: аргументы триггера, старая/новая строка, перезапись строки (где это разрешено) и вся стандартная информация о триггере;
- функции, возвращающие множество, сопоставлены с итераторами в C# и последовательностями в F#;
- табличные функции, а также функции, возвращающие записи или наборы записей;
- полная поддержка функций IN/OUT/INOUT.

Поддержка типов данных
Поддерживаются 40 типов PostgreSQL, все из которых сопоставлены со стандартными типами dotnet NPGSQL. Единственными исключениями являются типы multirange, enum и struct, которые планируют добавить в будущем. Все типы данных допускают значение NULL, имеют полную поддержку массивов и полностью протестированы на C# и F#.

SPI (Server Programming Interface)
SPI pl/dotnet использует клиентскую библиотеку NPGSQL для обеспечения собственной реализации dotnet, максимально совместимой с существующим клиентским кодом. Вызовы NPGSQL перехватываются на очень низком уровне, чтобы заменить обработку клиентского протокола вызовами SPI; в остальном NPGSQL не был изменен. Авторы использовали набор тестов NPGSQL для тестирования пакета, что дало им хорошее представление об уровне совместимости.

Инструкции по установке находятся здесь.
Проект на GitHub.
Авторы приветствуют участие и отзывы сообщества в дискуссиях на GitHub.

Источник: https://www.postgresql.org/about/news/announcing-pldotnet-version-099-beta-2838/
👍31👎1
День 1920. #УрокиРазработки
Уроки 50 Лет Разработки ПО


Урок 7. Запись знаний дешевле, чем повторное их обретение
Приходилось ли вам исследовать существующие системы, чтобы выяснить, как их изменить? А кто из вас записывал всё, что узнал, для дальнейшего использования?

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

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

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

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

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

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

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

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 2.
👍16
День 1921. #ЗаметкиНаПолях
Не Изменяйте Строки Подключения Вручную
Вам когда-нибудь приходилось изменять строку подключения в коде? Вы использовали регулярное выражение или другие манипуляции со строкой? Есть способ лучше.

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

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

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

Лучше использовать построитель строки подключения. Это классы, которые наследуют от базового System.Data.Common.DbConnectionStringBuilder. Например, System.Data.SqlClient.SqlConnectionStringBuilder используется для подключения к Microsoft SQL Server, но есть версии и для других СУБД.

Сначала нам нужен доступ к значению строки подключения (она должна поступать из системы конфигурации приложения):
var connStr = "Data Source=myServerAddress;Initial Catalog=db_1;User Id=my_user;Password=Pa55w0rd!;";

Это значение может быть использовано как аргумент конструктора построителя строк:
var builder = 
new SqlConnectionStringBuilder(connStr);

Мы можем изменять любые значения в строке подключения. Здесь обновим имя БД или исходный каталог:
builder.InitialCatalog = "new_db";

Теперь можно получить доступ к обновлённой строке подключения и использовать её:
var newConnStr = builder.ConnectionString;

Обновлённое значение будет:
Data Source=myServerAddress;Initial Catalog=new_db_name;User ID=my_user;Password=Pa55w0rd!

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

Источник: https://adamstorr.co.uk/blog/stop-using-regex-for-updating-connection-strings/
👍20
День 1922. #юмор
👍24👎1
День 1923. #ЗаметкиНаПолях
Отображаем Математические Формулы в HTML

Это довольно редкий случай (в зависимости от вашего домена), но иногда бывает нужно вывести на экран математическую формулу (и порой довольно сложную). На протяжении многих лет существовали различные рекомендации и спецификации W3C для языка математической разметки. MathML разработан как универсальная спецификация для математической записи во многих различных областях: браузеры, офисные пакеты, программы чтения EPUB, генераторы на основе LaTeX и т.п.

Самый ранний язык математической разметки (MathML) 1 был рекомендован в 1998 году и даже был включен в Mozilla 1.0. С тех пор появились ещё две версии MathML, причем второе издание MathML 3.0 было утверждено в качестве стандарта ISO в 2015 году.

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

MathML Core — это попытка создать минимальную версию MathML, хорошо совместимую с современной веб-платформой с упором на тестируемость и совместимость. MathML 3 содержал 195 элементов; MathML Core ориентирован на около 30!

Язык представляет собой набор HTML элементов, заключённый в общего родителя – элемент <math>. Например, вот так будет выглядеть дробь «x в квадрате, делённое на 3» (к сожалению, Телеграм пока не поддерживает MathML 😊):
<!-- родительский элемент -->
<math>
<!-- дробь: 1й дочерний элемент – числитель, 2й - знаменатель -->
<mfrac>
<!-- Число (1й дочерний) в степени (2й) -->
<msup>
<!-- mi - идентификатор (переменная или функция) -->
<mi>x</mi>
<!-- mn – числовая константа -->
<mn>2</mn>
</msup>
<mrow>
<mn>3</mn>
</mrow>
</mfrac>
</math>

Поскольку это HTML-элементы, они позволяют включать в себя и другие HTML-элементы, например, поля для ввода (если вы, например, создаёте форму для теста по математике), а также позволяют использовать CSS для стилизации.

Единственная проблема в том, что писать элементы MathML некрасиво и довольно утомительно. Одной из самых популярных альтернатив является MathJax, который включает в себя целый движок отображения на JavaScript. Если вы разместите сценарий MathJax на своей странице, он просканирует HTML-код и заменит блоки с математическими выражениями эквивалентом в MathML. MathJax использует выражения TeX/LaTeX (внутри блоков $$..$$), которые гораздо короче и проще для понимания, чем MathML. Например, вот формула корней квадратного уравнения:
$$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$


AsciiMath идёт ещё дальше, и позволяет писать более понятные выражения, как если бы мы писали их на одном из языков программирования. Например, вот та же формула корней квадратного уравнения в AsciiMath:
`x = (-b-sqrt(b^2-4ac))/(2a)`

MathJax поддерживает и формат AsciiMath.

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

Источник: https://andrewlock.net/rendering-math-in-html-mathml-mathml-core-and-asciimath/
👍12
День 1924. #Книги
Пособие по Основам Разработки
Тут в одном из чатов выложили преинтересную ссылку. В Майкрософт создали целое пособие по разработке и выложили его для всеобщего обозрения.

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

Здесь есть множество полезных статей, шпаргалок и чек-листов, например:
- Структура спринта: как подготовить проект и команду, какая работа делается на каждом этапе спринта.
- Чек-лист основ разработки: настроена ли система управления версиями, настроено ли тестирование и CI/CD, согласованы ли обзоры кода, учтена ли безопасность, настроен ли мониторинг системы
- Документация по Agile
- Документация по видам автоматизированного тестирования
- Советы и инструменты для проведения обзоров кода
- и многое, многое другое.

В общем, настоятельно советую к прочтению и себе отложил в закладки.
👍28
День 1925. #ЗаметкиНаПолях
Полезные функции EF Core 8. Начало
Рассмотрим набор функций Entity Framework Core 8, которые могут быть полезны многим.

1. Поддержка сопоставления коллекций примитивов
Коллекция примитивов - это набор значений примитивного типа для провайдера БД (обычно это числа и строки, перечисления). Раньше этого можно было достичь с помощью специального кода, а теперь это доступно прямо из коробки. Например, теперь можно легко сопоставить свойство:
public List<string> Tags { get; }

с его конфигурацией
builder.Property(o => o.Tags);

Тогда для следующей таблицы:
CREATE TABLE "Tweets" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_Tweets" PRIMARY KEY AUTOINCREMENT,
"Name" TEXT NOT NULL,
"Tags" TEXT NOT NULL
);

EF сохранит коллекцию значений в (в данном случае) одной строке и распарсит их обратно в список объектов, которые вы ожидаете при запросе из БД, что довольно удобно. Это также будет работать для таких типов, как uint, bool, DateOnly, DateTime, Uri.
Uri? Да, функция будет работать и для типов, для которых в EF есть встроенные преобразователи!

2. Сложные типы
Говоря о типах объектов, которые хранятся и сопоставляются с реляционными БД с помощью Entity Framework, можно грубо выделить 3 категории:
- Типы с одним значением: int, string, Guid, DateTime, и т.п.
- Структурированные объекты, однозначно определённые с помощью ключа (сущности).
- Структурированные объекты, не определяемые ключом (типы-значения).

Начиная с EF Core 8 появился хороший способ сопоставления третьей категории объектов. Рассмотрим простой пример модели:
public class Price
{
public decimal Amount { get; set; }
public string Currency { get; set; }
}

public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public Price Price { get; set; }
}

Конфигурация для сущности Product может выглядеть так:
public void Configure(EntityTypeBuilder<Product> b)
{
b.ToTable("Products");
b.HasKey(p => p.Id);

b.ComplexProperty(p => p.Price);
}

Мы получим примерно такую таблицу:
CREATE TABLE "Products" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_Products" PRIMARY KEY AUTOINCREMENT,
"Name" TEXT NOT NULL,
"Price_Amount" TEXT NOT NULL,
"Price_Currency" INTEGER NOT NULL
);

Как видим, EF Core автоматически объединит свойства сложного объекта в соответствующее количество столбцов.

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

Пример выше может привести к проблемам из-за изменяемости объекта Price. Если продукт имеет две цены (например, розничную и оптовую), которым мы изначально назначили один и тот же экземпляр Price, при изменении значения одной цены результирующий оператор SQL фактически изменит обе. Эту проблему можно легко обойти, сделав базовую сущность Price неизменяемой (или записью). Вот тестовый пример этого поведения.

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

Источник:
https://dateo-software.de/blog/entity-framework-8
👍11
День 1926. #ЗаметкиНаПолях
Полезные функции EF Core 8. Окончание

Начало

3. Столбцы JSON
Сопоставление столбцов JSON уже было частью EF Core 7, поэтому оно не совсем новое, но есть некоторые улучшения. Предположим, мы создаём некий код авторизации с настраиваемыми ролями и политиками:
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public IList<CustomPolicy>
Policies { get; set; };
}

public class CustomPolicy
{
public string Key { get; set; }
public bool Value { get; set; }
}

Чтобы сопоставить эти сущности с одной таблицей, а Policies - с одним столбцом, конфигурация будет такой:
public void Configure(EntityTypeBuilder<Role> b)
{
b.ToTable("Roles");
b.HasKey(o => o.Id);

b.OwnsMany(o => o.Policies, x =>
{
x.Property(p => p.Key);
x.Property(p => p.Value);
x.ToJson();
});
}

При создании таблицы свойство Policies будет текстовым столбцом, содержащим JSON. Это было и в EF Core 7. В 8й версии поддерживаются запросы с использованием индексов массивов JSON, трансляция запросов к коллекциям JSON, содержащим сложные типы, запросы и обновления отдельных полей JSON. Также JSON колонки доступны в SQLite.

4. Контрольные значения
Контрольное значение заставляет EF Core проверять заданное значение вместо значения по умолчанию типа для свойства, которое необходимо установить. Предположим, что мы моделируем сущность ToDoItem:
public class ToDoItem
{
public int Id { get; set; }
public string Name { get; set; }
public int Priority { get; set; }
}

По бизнес-правилам приоритет по умолчанию должен быть 100:
public void Configure(EntityTypeBuilder<ToDoItem> b)
{
b.ToTable("ToDoItems");
b.HasKey(o => o.Id);

b.Property(o => o.Priority)
.HasDefaultValueSql(100);
}

Т.к. свойство Priority не обнуляемое, а значение по умолчанию для int равно 0, у нас не будет возможности сохранить экземпляр ToDoItem со значением 0. EF Core идентифицировал бы 0 как значение по умолчанию, предположил, что свойство не установлено, и установил бы его в 100.

Если сделать свойство обнуляемым, то по умолчанию оно будет null. Но это заставит нас учитывать в коде, что Priority может null. В EF Core 8 можно настроить контрольное значение, т.е. значение, которое сообщает EF Core, что вместо него нужно вставить в БД значение по умолчанию:

b.Property(o => o.Priority)
.HasSentinel(-1)
.HasDefaultValueSql(100);

Здесь мы проверяем контрольное значение -1 (которое не используется в бизнес-логике), поэтому сохранение экземпляра с приоритетом 0 фактически становится возможным без костылей в реальной модели объекта.

Источник: https://dateo-software.de/blog/entity-framework-8
👍17
Какое свойство демонстрирует класс или метод, чей код тесно связан с единственной, хорошо определённой задачей?
#Quiz #BestPractices
Anonymous Quiz
25%
Сильная связанность (Tight coupling)
18%
Низкая изменчивость (Low variability)
14%
Слабая связанность (Loose coupling)
43%
Высокая связность (High cohesion)
👎22👍11
День 1927. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 8. Главное требование к разработке — налаженное и эффективное общение
Разработка ПО связана как с вычислениями, так и с общением. А разработка требований полностью основана на общении. Члены команды, которые отвечают за соблюдение требований (чаще их называют бизнес-аналитиками), находятся в центре сети общения. Они координируют обмен знаниями о требованиях между всеми участниками проекта. Бизнес-аналитик должен сообщать всем участникам информацию о требованиях, приоритетах, состоянии и изменениях.

Разные аудитории, разные потребности
Бизнес-аналитик может ссылаться на документы, например, описывающие бизнес-правила или содержащие информацию о похожих продуктах. Он должен оценить, классифицировать и записать всю информацию в соответствующей письменной форме, т.к. она должна быть передана клиентам для проверки, а и затем разработчикам — для воплощения.

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

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

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

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

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

Существует множество стандартных обозначений для анализа диаграмм и моделей, в том числе:
- структурированный анализ;
- IDEF0;
- унифицированный язык моделирования (Unified Modeling Language, UML);
- язык моделирования требований (Requirements Modeling Language, RML).

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

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 2.
👍1
День 1928. #МоиИнструменты
Обновляем Устаревшие Пакеты в .NET

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

1. Диспетчер пакетов в IDE
В Visual Studio или Rider легко узнать, доступны ли новые версии пакетов, используемых вашим проектом, с помощью диспетчера пакетов NuGet.
В Visual Studio откройте «Tools > NuGet Package Manager > Manage NuGet Packages for Solution…» (Инструменты > Менеджер NuGet-Пакетов > Управлять NuGet-Пакетами в Решении…). На вкладке «Updates» (Обновления) выберите все обновления и нажмите «Обновить». Либо вы можете обновить их по одному. Кроме того, вкладка Consolidate (Консолидация) покажет пакеты, которые установлены в разных версиях в проектах решения и позволит привести все установленные пакеты к одной версии.
В Rider откройте «View > Tool Windows > NuGet» (Вид > Окна Инструментов > NuGet). Чтобы обновить установленные пакеты, нажмите «Upgrade Packages in Solution» (Обновить пакеты в решении) на панели инструментов окна NuGet, а затем выберите, какие пакеты следует обновить. Либо вы можете выбрать один из установленных пакетов в левой части окна NuGet, выбрать нужную версию в правой части, а затем обновить версию пакета в конкретных проектах.

2. Глобальная утилита dotnet-outdated
Это утилита командной строки с открытым исходным кодом. Для начала нужно её установить, выполнив следующую команду:
dotnet tool install --global dotnet-outdated-tool

Теперь вызовите её, выполнив команду в папке проекта или решения:
dotnet-outdated

Утилита выдаст список всех доступных обновлений, выделенных цветом:
- зеленым – патчи и исправления ошибок (обратно совместимо),
- желтым – обновления минорных версий (обратно совместимо, добавлены новые функции),
- красным – обновления мажорных версий (возможны ломающие изменения).
Чтобы обновить все устаревшие пакеты, выполните команду:
dotnet-outdated –upgrade


3. Через команду dotnet
Также можно использовать утилиты командной строки dotnet напрямую. Чтобы получить список устаревших пакетов, выполните следующую команду:
dotnet list package --outdated

Чтобы обновить устаревший пакет, нужно выполнить команду:
dotnet add package PACKAGENAME

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

Если ваш проект размещён на GitHub, не обязательно постоянно помнить о необходимости проверки обновлений. Утилиты GitHub, вроде Dependabot помогут вам в этом, проанализировав зависимости ваших проектов и создавая пул-реквесты каждый раз, когда появляется новая версия какой-то из зависимостей. См. подробнее

Источник: https://bartwullems.blogspot.com/2024/05/net-core-view-outdated-packages.html?m=1
👍18
День 1929. #Карьера
Наибольшее Влияние на Качество Кода

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

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

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

Избегайте режима аврала!

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

Исследования показали, что разработчики, которые много работают в течение длительного времени, что приводит к лишению сна, на 44% менее продуктивны. Сектор, печально известный режимами аврала, — игровая индустрия. Многие игры, в которых разработчики были вынуждены перерабатывать, чтобы укладываться в сроки, приводили к выпуску продуктов с ошибками и плохим отзывам.

Аврал – это провал в управлении проектом.

Ян Шрайбер, доцент Рочестерского технологического института, рассказал о физиологических эффектах аврала. Он говорит, что дело может дойти даже до того, что ваша продуктивность уйдёт в минус: «Когда вы проработаете более 60 часов в неделю, ваши когнитивные функции станут хуже, чем у человека, который вообще не работал.»

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

Источник: https://bartwullems.blogspot.com/2024/05/the-biggest-effect-on-code-quality.html
👍19
День 1930. #ЗаметкиНаПолях
Организуем Минимальные API в
ASP.NET Core
Часто говорят, что нельзя использовать минимальные API для «реальных» приложений. Скорее всего, причина в том, что большинство примеров очень просты и не показывают, как организовать код так, чтобы это имело смысл для более крупного приложения.

Проблема в том, что в ASP.NET все примеры заканчиваются некоторыми конечными точками в Program.cs со встроенным кодом (как показано ниже), и на самом деле это не то, каким вы хотите видеть код в более крупном приложении:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/todos", async (TodoDb db) =>
await db.Todos.ToListAsync());
app.MapGet("/todos/{id}",
async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());

app.Run();

Здесь мы используем пример из руководства Microsoft. Итак, рассмотрим, как организовать код в что-то более удобочитаемое.

1. Используем методы расширения для организации конечных точек
В папке Endpoints или Todos (если вы используете чистую архитектуру) создадим файл TodoEndpoints.cs:
public static class TodoEndpoints
{
public static void
RegisterTodoEndpoints(this WebApplication app)
{
app.MapGet("/todos", async (TodoDb db) =>
await db.Todos.ToListAsync());

app.MapGet("/todos/{id}",
async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());

}
}

Это сразу сделает файл Program.cs намного чище:

var app = builder.Build();

app.RegisterTodosEndpoints();
app.Run();

Также мы можем разделить группы конечных точек по файлам.

2. Используем TypedResults вместо Results
TypedResults позволит нам не использовать атрибут или метод Produces для описания типа возвращаемого значения для OpenAPI/Swagger. Тип будет выведен из типа возвращаемого значения:
app.MapGet("/todos/{id}", 
async Task<Results<Ok<Todo>, NotFound>> (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? TypedResults.Ok(todo)
: TypedResults.NotFound());


3. Отделим функциональность от регистрации
Лямбда–выражение из метода конечной точки можно вынести в отдельный метод, что улучшит читаемость и тестируемость:
app.MapGet("/todos/{id}", GetById);

static async Task<Results<Ok<Todo>, NotFound>>
GetById(int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? TypedResults.Ok(todo)
: TypedResults.NotFound();

Тогда регистрация TodoEndpoints будет выглядеть так:
app.MapGet("/todos", GetAll);
app.MapGet("/todos/{id}", GetById);

// методы

Теперь в тестах методы можно вызывать напрямую, а не через API.

4. Группируем
Конечные точки можно сгруппировать, чтобы избежать повторения кода:
var todos = app.MapGroup("/todos");

todos.MapGet("/", GetAll);
todos.MapGet("/{id}", GetById);

Также к группе можно применять общие политики, тэги и т.п.
var todos = app.MapGroup("/todos")
.RequireAuthorization()
.WithTags("Todos");

Источник: https://www.tessferrandez.com/blog/2023/10/31/organizing-minimal-apis.html
👍43👎1
День 1931. #TipsAndTricks
Обновляем Сертификат для localhost в .NET Core

Многие команды не используют https на машинах разработчиков, поскольку не хотят возиться с сертификатами. Хорошей новостью является то, что добавить или обновить сертификат локального хоста в случае истечения срока при использовании Kestrel очень просто. Для управления самоподписанным сертификатом можно использовать встроенную команду dotnet dev-certs.

При необходимости сначала можно удалить существующий сертификат:
dotnet dev-certs https --clean

Вывод:
Cleaning HTTPS development certificates from the machine. A prompt might get displayed to confirm the removal of some of the certificates.
HTTPS development certificates successfully removed from the machine.

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


Теперь сгенерируем новый самоподписанный сертификат, и сразу добавим его в доверенные:
dotnet dev-certs https --trust

Вывод:
Trusting the HTTPS development certificate was requested. A confirmation prompt will be displayed if the certificate was not previously trusted. Click yes on the prompt to trust the certificate.
Successfully created and trusted a new HTTPS certificate.

(Было запрошено доверие к сертификату разработки HTTPS. Если сертификат ранее не был доверенным, отобразится запрос на подтверждение. Нажмите «Да» в запросе, чтобы доверять сертификату.
Успешно создан новый доверенный сертификат HTTPS.)

Наконец, следующая команда проверяет существующие сертификаты:
dotnet dev-certs https --check

Output:
A valid certificate was found: 189E61FFAD59C21110E9AD13A009B984EE5E8D5D - CN=localhost - Valid from 2024-04-22 13:11:50Z to 2025-04-22 13:11:50Z - IsHttpsDevelopmentCertificate: true - IsExportable: true

Run the command with both --check and --trust options to ensure that the certificate is not only valid but also trusted.

(Был найден действительный сертификат: 189E61FFAD59C21110E9AD13A009B984EE5E8D5D - CN=localhost - действителен с 22 апреля 2024 г., 13:11:50Z по 22 апреля 2025 г., 13:11:50Z - IsHttpsDevelopmentCertificate: true - IsExportable: true

Запустите команду с параметрами --check и --trust, чтобы убедиться, что сертификат не только действителен, но и добавлен в доверенные.)


Источник: https://bartwullems.blogspot.com/2024/05/net-core-renew-localhost-certificate.html
👍51