Опытный разработчик не так давно зашёл в .Net и поставил цель получить сертификат Microsoft. Свой ежедневный прогресс он описывает на канале .Net Разработчик. Заметки об изученном материале, советы по повышению производительности и поддержке мотивации, ин
<кусок кода>» Скорее всего, автор скопирует и вставит его себе, не задумываясь. И в дальнейшем не будет сильно беспокоиться о качестве своего кода, если будет знать, что вы за него всё исправите. Правильно в этом случае было бы описать, как должен выглядеть метод, объяснить, почему код не сработает, и как бы вы подошли к решению проблемы. Иногда допустим псевдокод. 4. Не откладывайте обзор Очень важно не блокировать работу друг друга. Переключение контекста — непростая задача для разработчика. И в ожидании обзора кода по одной из своих задач автору придётся переключиться на другую, и переключаться туда и обратно, по мере получения комментариев и ответа на них. Это в любом случае произойдет, даже если проверка пройдёт очень быстро. Однако, чем дольше затянется обзор, тем больше таких переключений будет происходить. Кроме того, вы можете и фактически блокировать чью-то работу. Т.е. люди не смогут продолжать работу, пока эта функция не будет завершена и изменения кода не будут объединены. Всё это неэффективно и раздражает обе стороны. Чтобы убедиться, что такого не произойдёт, всегда полезно отдавать приоритет проверкам кода в своем расписании. Конечно, у вас могут быть более срочные задачи, но вы обязаны найти правильный баланс между собственной работой и обзорами. Окончание следует… Источник: betterprogramming.pub/10-tips…25aa22c5
byte[] data = …;
using (Aes aes = Aes.Create())
{
byte[] key = …;
byte[] iv = …;
using ICryptoTransform transform = aes.CreateEncryptor(key, iv);
byte[] encrypted = transform.TransformFinalBlock(data, 0, data.Length);
}
Тут от силы десяток строк кода, но не все из них очевидны. Что такое transform? Что за финальный блок?
API предоставляют довольно много функций и имеют сбивающие с толку названия. Например, TransformFinalBlock несмотря на то, что в его названии есть «Block», почти всегда будет способен шифровать более одного блока. Это также означает, что данные не нужно выравнивать по блокам. Поскольку ничего из этого невозможно понять, разработчики часто обходят предполагаемые проблемы, например, обрабатывают отдельные блоки. Хотя этот дизайн API предлагает наибольшую гибкость для разработчиков, он также предлагает наибольшую сложность.
Простые API важны для защиты от неправильного использования, и .NET стал лучше в этом отношении за последние несколько выпусков, предложив для Aes, TripleDES, и т.п. алгоритмов API с вызовом единственного метода, вроде EncryptCbc, EncryptEcb или DecryptCbc и DecryptEcb:
byte[] data = …;
using (Aes aes = Aes.Create())
{
byte[] key = …;
byte[] iv = …;
aes.Key = key;
// Шифруем все данные за раз
byte[] encrypted = aes.EncryptCbc(data, iv);
}
Здесь нет ICryptoTransform и нет необходимости беспокоиться о блоках, отступах и т. д.
Кроме того, некоторые методы сделали статическими. Хеширование до .NET 5 это выглядело примерно так:
byte[] data = …;
using (SHA256 hash = SHA256.Create())
{
byte[] digest = hash.ComputeHash(data);
}
Теперь вместо получения экземпляра алгоритма, есть статический метод:
byte[] digest = SHA256.HashData(data);Для .NET 6 API хеширования также были перенесены в классы HMAC, предлагая такие же улучшенные API и более высокую производительность. PBKDF2 также улучшил производительность в .NET 6:
byte[] salt = RandomNumberGenerator.GetBytes(32); byte[] prk = Rfc2898DeriveBytes.Pbkdf2( userPassword, salt, iterations: 200_000, HashAlgorithmName.SHA256, outputLength: 32);Обновлённые API позволяют работать с
ReadOnlySpan<byte> для входных данных и возможность записи в Span<byte> для выходных данных.
Источник: https://vcsjones.dev/one-shot-crypto/HostBuilder для поддержки HostFactoryResolver, используемого инструментами EF Core. Это было достигнуто за счёт добавления дополнительных событий DiagnosticSource в HostBuilder. Они позволяют HostFactoryResolver получить доступ к HostBuilder без необходимости использовать соглашения предыдущих версий.
Инструментам EF Core просто необходимо получить доступ к построенному IHost, чтобы извлечь из него IServiceProvider, поэтому запущенное фоновое приложение останавливалось. Однако WebApplicationFactory должна иметь возможность модифицировать HostBuilder вашего приложения до вызова Build(). Кроме того, ей требуется, чтобы приложение продолжало работать для отправки в него тестовых запросов.
WebApplicationFactory предоставляет несколько способов настройки вашего приложения в интеграционных тестах, но по своей сути он предоставляет способ запуска экземпляра хоста вашего приложения в памяти. Один из основных методов в этом процессе — EnsureServer(). Он отвечает за создание тестового сервера, предварительно получая экземпляр IHostBuilder. IHostBuilder он пытается получить через Program.CreateHostBuilder(), обычно используемый в ASP.NET Core 3.x/5. Если это не удаётся, он ищет метод Program.CreateWebHostBuilder(), используемый в ASP.NET Core 2.x. Если и это не удается, он прибегает к подходу .NET 6.
В этом подходе используется новый тип DeferredHostBuilder. DeferredHostBuilder предназначен для «захвата» вызываемых в нём методов конфигурации (например, ConfigureServices()), а затем «воспроизведения» их для IHostBuilder реального приложения, как только он станет доступен. Методы «отсрочки» собирают методы конфигурации в виде мультикаст-делегата, а делегаты затем применяются к IHostBuilder, когда вызывается событие HostBuilding экземляра DiagnosticSource.
Затем запускается процесс, описанный в предыдущем посте, в котором приложение выполняется в отдельном потоке, с помощью событий DiagnosticSource вызывается настройка ConfigureHostBuilder() и возвращается экземпляр IHost. При этом приложение в отдельном потоке не перестаёт работать, потому что нам нужно, чтобы остальной код в Program.cs приложения выполнился. DeferredHostBuilder сохраняет IHost в новый тип DefferedHost.
DeferredHost отвечает за ожидание правильного запуска приложения. Ему нужно подождать, пока будут настроены все конечные точки, а также исполнится любой дополнительный стартовый код. Это достигается через существующие события IHostApplicationLifetime, которые вызываются в обычном приложении на универсальном хосте при запуске. В частности, вызов NotifyStarted() вызывает событие ApplicationStarted, которое DeferredHost использует для определения того, что приложение запущено, и можно безопасно запускать тесты. См. рисунок ниже.
После этого WebApplicationFactory создаёт HttpClient, как и в предыдущих версиях, и вы можете выполнять вызовы к приложению в памяти, как и раньше. Стоит знать, что (в отличие от предыдущих версий ASP.NET Core) в ваших тестах будет исполнено всё, что содержится в Program.cs приложения. Но, помимо этого нюанса, всё в вашем тестовом коде останется прежним.
Подробный перевод статьи с примерами кода размещён на Хабре.
Источник: andrewlock.net/explori…licationDateTime повсеместно используется как тип свойств сущностей и полей базы данных, но если вас действительно волнует временная часть значения, она часто неоднозначна. В каком часовом поясе указана дата? Она сохраняется как UTC? Всегда? Вы уверены? DateTimeOffset решает эту проблему.
Хранение значений DateTime
Тип DateTime всегда подразумевает время локальной машины. Когда вы запрашиваете .Today или .Now, он использует локальные системные часы. Это может легко вызвать проблемы между машинами с разным временем и/или часовыми поясами:
var now = new DateTime(2022,1,20,18,11,30); Console.WriteLine(now); // 1/20/2022 6:11:30 PM Console.WriteLine(now.ToLocalTime()); // 1/20/2022 9:11:30 PM Console.WriteLine(now.ToUniversalTime()); // 1/20/2022 3:11:30 PMИзначальное время — 18:11 20 января 2022 года на моем компьютере в московском часовом поясе. Переменная now не содержит данных о часовом поясе. Функции
ToLocalTime и ToUniversalTime предполагают, что данные, с которыми они работают, представлены в формате UTC или по местному времени соответственно. В результате время, полученное с помощью ToLocalTime, неверно, так как оно добавляет 3 часа к фактическому времени 18:11.
Обратите внимание, что, когда вы сохраняете дату и время в своём приложении, они будут храниться относительно часового пояса машины, если только вы не будете следить за использованием времени UTC везде. Но даже в этом случае постфактум, глядя на данные, нельзя будет сказать, точно ли это время по UTC.
Что произойдёт, когда один и тот же код запустится на машинах разработчиков в разных часовых поясах? Что произойдёт при запуске тестов? А если вы переместите своё облачное приложение из одного региона в другой?
По всем этим причинам может оказаться целесообразным хранить даты в вашем приложении, используя тип DateTimeOffset.
DateTimeOffset является типом как в .NET, так и в SQL Server (в других базах данных тоже есть эквиваленты). Основное отличие между ним и более простым DateTime в том, что он включает смещение часового пояса от UTC. Таким образом, при взгляде на DateTimeOffset всегда ясно, какое время имеется в виду, будь то UTC или местное время.
Добавим к примерам выше аналоги с DateTimeOffset:
var nowHere = new DateTimeOffset(now); Console.WriteLine(nowHere); // 1/20/2022 6:11:30 PM +03:00 Console.WriteLine(nowHere.ToLocalTime()); // 1/20/2022 6:11:30 PM +03:00 Console.WriteLine(nowHere.ToUniversalTime()); // 1/20/2022 3:11:30 PM +00:00Мы видим 2 изменения: - значение
ToLocalTime правильное,
- строковые представления даты включают смещение часового пояса.
DateTimeOffset в SQL Server
DateTimeOffset использует переменную точность и поэтому может занимать больше места, чем DateTime. Если вы хотите увидеть разницу, выполните следующие запросы:
select GetDate() select SYSDATETIME() select SYSDATETIMEOFFSET()Результаты (заметьте разницу в точности):
2022-01-20 18:30:18.227 2022-01-20 18:30:18.2292271 2022-01-20 18:30:18.2292271 -05:00EF Core прекрасно поддерживает
DateTimeOffset.
Итого
Значения DateTime не имеют сведений о смещении часового пояса или его отсутствии (в UTC). Если вам нужно знать, когда что-то произошло на самом деле, с большей точностью, чем просто приблизительная дата, и вы не можете быть на 100% уверены, что ваши даты ВСЕГДА хранятся в формате UTC, вам следует рассмотреть возможность использования DateTimeOffset для представления значений даты и времени. Этот тип указывает смещение, таким образом, может устранить множество ошибок, от которых страдают многие системы реального мира.
Источник: https://ardalis.com/why-use-datetimeoffset/cosign generate-key-pairкоторый вы сохранили в секретах репозитория GitHub Actions под именем
SIGNING_SECRET. Подписать контейнер в рабочем процессе можно, выполнив что-то вроде:
...
jobs:
build:
steps:
# ... шаги сборки
- uses: sigstore/cosign-installer@main
- name: Write signing key to disk (only needed for `cosign sign --key`)
run: echo "${{ secrets.SIGNING_SECRET }}" > cosign.key
- name: Sign container image
run: |
cosign sign --key cosign.key \
-a "repo=${{ github.repository }}" \
-a "workflow=${{ github.workflow }}" \
-a "ref=${{ github.sha }}" \
ghcr.io/your-org/your-repo:some-tag
env:
COSIGN_PASSWORD: ""
Заметьте, что в подпись добавлены аннотации репозитория, рабочего процесса и ссылки на коммит. Вот и всё.
Затем проверить подпись после получения образа можно с помощью команды:
cosign verify --key cosign.pub ghcr.io/your-org/your-repo:some-tagА используя инструменты fulcio и Rekor от sigstore вы можете подписывать свои образы контейнеров с помощью токена OIDC, предоставленного GitHub, без необходимости предоставления и поддержки собственного закрытого ключа. Источник: github.blog/2021-12…-actions
Sales и Support, которые работают в своих соответствующих микросервисах (см. картинку ниже).
В обоих этих микросервисах есть одна и та же сущность клиента (Customer). Предположим также, что сервис продаж является вышестоящим по отношению к сервису поддержки. То есть, когда клиент создаётся в продажах, он передаётся в поддержку, которая создаёт собственное представление этого клиента.
Отсюда вопрос: «Должен ли сервис поддержки использовать тот же ID, что и сервис продаж, или нужно назначать собственный ID своей версии клиента?»
Если вы используете UUID(GUID), вы можете легко назначить раздельные ID клиента в обоих сервисах, и идентификаторы в двух ограниченных контекстах не будут пересекаться. Но хорошо ли это?
Нет. Используйте один и тот же идентификатор во всей вашей системе, если этот идентификатор создаётся одним из ваших микросервисов. ID — это одно из свойств, представляющих клиента. Идентификатор контролируется микросервисом, который создаёт экземпляры клиента. В нашем примере это сервис продаж. Новые клиенты создаются, когда ваша компания что-то им продаёт. Поэтому Sales — главный микросервис для идентификации всех клиентов. Только сервис Sales может назначать ID. Нижестоящие сервисы должны реплицировать его в свои базы данных, аналогично тому, как они реплицируют свойства Name или Email.
Когда вы смотрите на идентификаторы с этой точки зрения, становится ясно, что не имеет смысла назначать раздельные идентификаторы клиентов в нижестоящих микросервисах, так же как не имеет смысла назначать разные имена или адреса электронной почты. Пока только один микросервис контролирует время существования определённого объекта (то есть может создавать и удалять его), у вас всегда будет только один идентификатор в вашей системе, даже если эта система состоит из нескольких микросервисов.
А что, если идентификатор поступает из внешней системы?
Предположим, клиенты могут зарегистрироваться в вашей системе через свои социальные профили, такие как Facebook. В этом случае Facebook предоставит вашей системе собственный идентификатор.
Стоит ли использовать этот идентификатор Facebook как есть?
Нет, вам нужно создать собственный идентификатор клиента и сохранить идентификатор Facebook как отдельное (необязательное) поле. Разница здесь в том, что у вас нет контроля над идентификаторами, которые предоставляет вам Facebook. Facebook потенциально может изменить эти идентификаторы и испортить вашу систему.
Контракт между вашими микросервисами намного прочнее, чем между вашей системой и Facebook. Вы можете (и должны) поддерживать обратную совместимость между микросервисами в вашей системе. Нет никакой гарантии такой совместимости между вашей системой и Facebook.
Источник: https://enterprisecraftsmanship.com/
Автор оригинала: Владимир ХориковOperatingSystem:
- IsAndroid
- IsBrowser
- IsFreeBSD
- IsIOS
- IsLinux
- IsWindows
- и другие.
Полный список находится здесь
Кроме того, представлены новые атрибуты:
- [SupportedOSPlatform] для обозначения, что API специфично для какой-то платформы.
- [UnsupportedOSPlatform] для обозначения, что API не подходит для какой-то платформы.
См. картинку ниже.
Считается, что API без этих атрибутов работают на всех платформах. Анализатор совместимости с платформой включён в список анализаторов качества кода Roslyn. Начиная с .NET 5, он входит в пакет SDK для .NET и включен по умолчанию для .NET 5 и более поздних версий.
Подробнее о том, как анализатор определяет зависимость от платформы
В .NET 6 представлена концепция включения платформ, когда одна платформа может быть подмножеством другой платформы. Новые настраиваемые атрибуты для зависимых от платформы API:
- [SupportedOSPlatformGuard]
- [UnsupportedOSPlatformGuard]
Например, платформа iOS является подмножеством платформы MacCatalyst. Поэтому для [SupportedOSPlatformGuard("MacCatalyst")] метод OperatingSystem.IsIOS(), помеченный этим атрибутом проверит и iOS, и MacCatalyst.
Источник: twitter.com/okyrylc…95752960Dispose.
https://youtu.be/U74m6eVYSGsdraggable() is not a function”.
Заметил, что плагин основан на нескольких пакетах из сборки jQuery UI. Поискал draggable в нашей кастомной сборке, и её там не оказалось. Поэтому скачал новую сборку, где всё должно было быть. И ползунок заработал! Правда сломался остальной код JS, причём сразу в нескольких неожиданных местах.
Промучившись с этим около часа, стал искать другие варианты. Нашёл. Есть же специально обученный HTML элемент <input type=”range” /> Проблема в том, что он в разных браузерах выглядит совершенно по-разному. Но и это не беда. Всего какой-то сотней строчек CSS можно было заставить элемент выглядеть так, как тебе надо.
В общем, на всю эту настройку, проверку во всех доступных браузерах, включая IE (не спрашивайте!), переписывание уже готового кода разметки и JS под использование нового элемента и т.п. ушла бОльшая часть дня. Но к приходу коллеги вечером, всё было готово, и выглядело даже почти так же, как изначально одобренный вариант UI. Я молодец! Я решил проблему!
Коллега удивился, что плагин не заработал. Но ещё больше удивился я (хотя, «удивился» тут слишком мягкое слово), когда коллега написал, что у него плагин заработал сразу, и он откатит мои изменения. После пары минут шока я решил разобраться, в чём же дело. И довольно скоро выяснил, что эту самую функцию draggable вообще не надо было вызывать! В шаге 3 на странице документации был пример кода, использующий эту функцию. Но элементы из jQuery UI работали и без неё. Только подключения плагина на страницу было достаточно! Причём всё прекрасно работало сразу и везде, включая эмулятор и реальный телефон. Дел было на 2 минуты в ленивом темпе!
Конечно, в глазах коллеги, я, наверное, выглядел полным идиотом, которому нельзя доверить даже элементарную операцию. Поэтому я, как мог, попытался объяснить, как же это меня так переклинило, и почему я потратил целый день впустую. Он вроде даже понял (по переписке трудно судить). Но если уж под какой случай и подходит определение эпик фейл, то это был он.