День 1750. #ЧтоНовенького
Топ 10 Новых Функций .NET 8. Начало
Итак, .NET 8 выпущен. Это LTS версия - с долгосрочной поддержкой в течение как минимум трех лет с момента запуска. Рассмотрим 10 лучших новых функций .NET 8.
1. Производительность
Как обычно, много внимания было уделено повышению производительности. Стивен Тауб написал огромную статью, в которой описываются все улучшения.
Вот наиболее заметные:
- Очень много улучшений связано с усовершенствованной оптимизацией на основе профиля (Dynamic PGO). Это метод оптимизации JIT-компилятора, который позволяет JIT собирать дополнительную информацию об окружении (так называемом профиле) в коде, чтобы полагаться на неё позже и «пересобирать» методы на «горячем пути», делая их ещё эффективнее. Так виртуальные вызовы методов могут заменяться прямыми, условные операторы инвертироваться, чтобы чаще переходить по «более горячему пути», могут клонироваться циклы и т.п.
- List<T>.AddRange(IEnumerable<T>) был переписан для повышения производительности, когда последовательность не является ICollection<T>. В этом случае AddRange не может использовать свойство Count и приходится итерировать всю добавляемую коллекцию. В .NET 8 этот метод работает быстрее на 60%, по сравнению с .NET 7.
- Int32.ToString() ускорен благодаря кэшированию значений от 0 до 299. В результате для этих чисел ToString() работает быстрее почти на 90% и ничего не аллоцирует, но и для других чисел он ускорен примерно на 40%.
2. Динамическая Адаптация к Размерам Приложений (DATAS)
В .NET 8 в сборщик мусора добавлена функция DATAS (Dynamic Adaptation To Application Sizes). Она регулирует использование памяти приложением на основе размера текущих данных (LDS), который включает в себя долгоживущие данные и текущие данные во время события сборки мусора. У DATAS есть два основных варианта использования:
- Для изменяющихся рабочих нагрузок в средах с ограниченной памятью, таких как контейнерные приложения с ограничениями памяти. DATAS уменьшает или увеличивает размер кучи по мере необходимости.
- Для небольших рабочих нагрузок с использованием Server GC - обеспечивает соответствие размера кучи фактическим требованиям приложения.
Основная задача DATAS — адаптация к размеру приложения. Сборщик мусора всегда динамически изменял параметры своей работы, но DATAS настраивает его так, чтобы он лучше соответствовал рабочим нагрузкам. См.
подробнее о DATAS.
3. Сериализация и десериализация в System.Text.Json
.NET 8 повышает удобство использования приложений Native AOT благодаря множеству улучшений в генераторах кода, включая:
- Поддержку сериализации типов с required и init свойствами.
- Атрибут JsonSourceGenerationOptions соответствующий JsonSerializerOptions, позволяющий настраивать сериализацию во время компиляции.
- Улучшенную обработку типов: игнорирует недоступные свойства, позволяет вкладывать объявления JsonSerializerContext и динамически обрабатывает типы, созданные компилятором.
- JsonStringEnumConverter<TEnum> новый тип преобразователя упрощающий сериализацию перечислений в нативных AOT-приложениях.
- Свойство JsonConverter.Type позволяющее получить тип неуниверсального экземпляра JsonConverter с поддержкой значений NULL для различных сценариев.
Множество других улучшений, в том числе:
- JsonNamingPolicy теперь включает новые политики именования для преобразования имён свойств в Snake_case и Kebab-Case.
- Десериализация данных в readonly поля или свойства, не имеющие сеттера.
- Отключение сериализатора на основе рефлексии по умолчанию. Эта возможность полезна для предотвращения непреднамеренного включения компонентов рефлексии, особенно в усеченных и нативных AOT-приложениях.
См. подробнее о новинках в System.Text.Json.
Продолжение следует…
Источник: https://blog.ndepend.com/net-8-top-10-new-features/
Топ 10 Новых Функций .NET 8. Начало
Итак, .NET 8 выпущен. Это LTS версия - с долгосрочной поддержкой в течение как минимум трех лет с момента запуска. Рассмотрим 10 лучших новых функций .NET 8.
1. Производительность
Как обычно, много внимания было уделено повышению производительности. Стивен Тауб написал огромную статью, в которой описываются все улучшения.
Вот наиболее заметные:
- Очень много улучшений связано с усовершенствованной оптимизацией на основе профиля (Dynamic PGO). Это метод оптимизации JIT-компилятора, который позволяет JIT собирать дополнительную информацию об окружении (так называемом профиле) в коде, чтобы полагаться на неё позже и «пересобирать» методы на «горячем пути», делая их ещё эффективнее. Так виртуальные вызовы методов могут заменяться прямыми, условные операторы инвертироваться, чтобы чаще переходить по «более горячему пути», могут клонироваться циклы и т.п.
- List<T>.AddRange(IEnumerable<T>) был переписан для повышения производительности, когда последовательность не является ICollection<T>. В этом случае AddRange не может использовать свойство Count и приходится итерировать всю добавляемую коллекцию. В .NET 8 этот метод работает быстрее на 60%, по сравнению с .NET 7.
- Int32.ToString() ускорен благодаря кэшированию значений от 0 до 299. В результате для этих чисел ToString() работает быстрее почти на 90% и ничего не аллоцирует, но и для других чисел он ускорен примерно на 40%.
2. Динамическая Адаптация к Размерам Приложений (DATAS)
В .NET 8 в сборщик мусора добавлена функция DATAS (Dynamic Adaptation To Application Sizes). Она регулирует использование памяти приложением на основе размера текущих данных (LDS), который включает в себя долгоживущие данные и текущие данные во время события сборки мусора. У DATAS есть два основных варианта использования:
- Для изменяющихся рабочих нагрузок в средах с ограниченной памятью, таких как контейнерные приложения с ограничениями памяти. DATAS уменьшает или увеличивает размер кучи по мере необходимости.
- Для небольших рабочих нагрузок с использованием Server GC - обеспечивает соответствие размера кучи фактическим требованиям приложения.
Основная задача DATAS — адаптация к размеру приложения. Сборщик мусора всегда динамически изменял параметры своей работы, но DATAS настраивает его так, чтобы он лучше соответствовал рабочим нагрузкам. См.
подробнее о DATAS.
3. Сериализация и десериализация в System.Text.Json
.NET 8 повышает удобство использования приложений Native AOT благодаря множеству улучшений в генераторах кода, включая:
- Поддержку сериализации типов с required и init свойствами.
- Атрибут JsonSourceGenerationOptions соответствующий JsonSerializerOptions, позволяющий настраивать сериализацию во время компиляции.
- Улучшенную обработку типов: игнорирует недоступные свойства, позволяет вкладывать объявления JsonSerializerContext и динамически обрабатывает типы, созданные компилятором.
- JsonStringEnumConverter<TEnum> новый тип преобразователя упрощающий сериализацию перечислений в нативных AOT-приложениях.
- Свойство JsonConverter.Type позволяющее получить тип неуниверсального экземпляра JsonConverter с поддержкой значений NULL для различных сценариев.
Множество других улучшений, в том числе:
- JsonNamingPolicy теперь включает новые политики именования для преобразования имён свойств в Snake_case и Kebab-Case.
- Десериализация данных в readonly поля или свойства, не имеющие сеттера.
- Отключение сериализатора на основе рефлексии по умолчанию. Эта возможность полезна для предотвращения непреднамеренного включения компонентов рефлексии, особенно в усеченных и нативных AOT-приложениях.
См. подробнее о новинках в System.Text.Json.
Продолжение следует…
Источник: https://blog.ndepend.com/net-8-top-10-new-features/
👍29👎1
День 1751. #ЧтоНовенького
Топ 10 Новых Функций .NET 8. Продолжение
Начало
4. Работа с коллекциями
Теперь синтаксис квадратных скобок можно использовать для инициализации многих видов коллекций (а также для объединения коллекций), в том числе коллекций только для чтения и неизменяемых коллекций, которые нельзя инициализировать с помощью старого синтаксиса фигурных скобок:
Также синтаксис [] можно использовать для инициализации пустой коллекции (в том числе словаря) вместо использования new List или Array.Empty.
К сожалению, к этому релизу не успели полноценно реализовать синтаксис инициализации словарей, вроде:
Но он есть в предложениях на C# 13.
Кроме того, появились удобные методы для случайного перемешивания элементов коллекции. Например, новые методы System.Random.GetItems() и System.Security.Cryptography.RandomNumberGenerator.GetItems() позволяют случайным образом выбирать указанное количество элементов из входного набора:
Новые методы Random.Shuffle и RandomNumberGenerator.Shuffle<T>(Span<T>) теперь позволяют рандомизировать элементы диапазона:
5. Новые абстракции времени
Недавно представленный класс TimeProvider и интерфейс ITimer предлагают абстракции времени, облегчая моделирование времени в сценариях тестирования. TimeProvider — это абстрактный класс с множеством виртуальных функций, что делает его идеальным кандидатом для интеграции с фреймворками моков.
Заметьте, что вы также можете использовать абстракцию времени для имитации операций Task, которые зависят от хода времени, например Task.Delay() и Task.WaitAsync().
См. также
- Подробнее про класс TimeProvider.
- Большая статья Эндрю Лока
6. Форматирование UTF8
Чтобы включить генерацию строкового представления вашего типа в целевом спане, реализуйте недавно представленный интерфейс IUtf8SpanFormattable для вашего типа:
Этот интерфейс похож на ISpanFormattable, но разработан специально для UTF-8 и Span<byte>, в отличие от UTF-16 и Span<char>. В .NET 8 все примитивные типы (и другие) реализуют этот интерфейс: Byte, Complex, Char, DateOnly, DateTime, DateTimeOffset, Decimal, Double, Guid, Half, IPAddress, IPNetwork, Int16, Int32, Int64, Int128, IntPtr, NFloat. , SByte, Single, Rune, TimeOnly, TimeSpan, UInt16, UInt32, UInt64, UInt128, UIntPtr и Version.
Разработан он в первую очередь для применения в minimal API и вебе (именно там мы чаще всего используем UTF-8). Вот пример:
Окончание следует…
Источник: https://blog.ndepend.com/net-8-top-10-new-features/
Топ 10 Новых Функций .NET 8. Продолжение
Начало
4. Работа с коллекциями
Теперь синтаксис квадратных скобок можно использовать для инициализации многих видов коллекций (а также для объединения коллекций), в том числе коллекций только для чтения и неизменяемых коллекций, которые нельзя инициализировать с помощью старого синтаксиса фигурных скобок:
// не компилируется
ImmutableList<int> list = { 1, 2, 3, 4 };
// работает
ImmutableList<int> list = [ 1, 2, 3, 4 ];
Также синтаксис [] можно использовать для инициализации пустой коллекции (в том числе словаря) вместо использования new List или Array.Empty.
К сожалению, к этому релизу не успели полноценно реализовать синтаксис инициализации словарей, вроде:
Dictionary<string, int> dict = ["a": 1, "b": 2];
Но он есть в предложениях на C# 13.
Кроме того, появились удобные методы для случайного перемешивания элементов коллекции. Например, новые методы System.Random.GetItems() и System.Security.Cryptography.RandomNumberGenerator.GetItems() позволяют случайным образом выбирать указанное количество элементов из входного набора:
ReadOnlySpan<int> numbers =
[1, 2, 3, 4, 5, 6];
var diceRolls =
Random.Shared.GetItems(numbers, 5);
// 5, 1, 1, 4, 4
Новые методы Random.Shuffle и RandomNumberGenerator.Shuffle<T>(Span<T>) теперь позволяют рандомизировать элементы диапазона:
Span<int> numbers = [1, 2, 3, 4, 5, 6];
Random.Shared.Shuffle(numbers);
// 3, 4, 1, 5, 2, 6
5. Новые абстракции времени
Недавно представленный класс TimeProvider и интерфейс ITimer предлагают абстракции времени, облегчая моделирование времени в сценариях тестирования. TimeProvider — это абстрактный класс с множеством виртуальных функций, что делает его идеальным кандидатом для интеграции с фреймворками моков.
Заметьте, что вы также можете использовать абстракцию времени для имитации операций Task, которые зависят от хода времени, например Task.Delay() и Task.WaitAsync().
См. также
- Подробнее про класс TimeProvider.
- Большая статья Эндрю Лока
6. Форматирование UTF8
Чтобы включить генерацию строкового представления вашего типа в целевом спане, реализуйте недавно представленный интерфейс IUtf8SpanFormattable для вашего типа:
public interface IUtf8SpanFormattable {
bool TryFormat(Span<byte> utf8Destination, out int bytesWritten, ReadOnlySpan<char> format, IFormatProvider? provider);
}
Этот интерфейс похож на ISpanFormattable, но разработан специально для UTF-8 и Span<byte>, в отличие от UTF-16 и Span<char>. В .NET 8 все примитивные типы (и другие) реализуют этот интерфейс: Byte, Complex, Char, DateOnly, DateTime, DateTimeOffset, Decimal, Double, Guid, Half, IPAddress, IPNetwork, Int16, Int32, Int64, Int128, IntPtr, NFloat. , SByte, Single, Rune, TimeOnly, TimeSpan, UInt16, UInt32, UInt64, UInt128, UIntPtr и Version.
Разработан он в первую очередь для применения в minimal API и вебе (именно там мы чаще всего используем UTF-8). Вот пример:
Span<byte> dest = stackalloc byte[16];
int written = 0;
// 123.456
123.456123.TryFormat(dest, out written, ".###");
// 2021-11-16
DateTime.Now.TryFormat(dest, out written, "yyyy-MM-dd");
Console.WriteLine(
Encoding.UTF8.GetString(dest.Slice(0, written)));
Окончание следует…
Источник: https://blog.ndepend.com/net-8-top-10-new-features/
👍15
День 1752. #ЧтоНовенького
Топ 10 Новых Функций .NET 8. Окончание
Начало
Продолжение
7. Потоковые методы ZipFile
Теперь можно сжимать файлы из каталога в поток без необходимости кэшировать их во временный файл. Это позволяет напрямую управлять результатом сжатия в памяти. Новый API полезен в сценариях, где дисковое пространство ограничено, поскольку устраняет необходимость использования диска в качестве промежуточного шага. Новые статические методы: ZipFile.CreateFromDirectory и ExtractToDirectory.
8. Новые типы, которые могут улучшить производительность в различных ситуациях
1) System.Collections.Frozen содержит новые классы коллекций FrozenSet<T> и FrozenDictionary<TKey,TValue>. Эти коллекции нельзя изменить после создания. Внутри реализация использует это требование, чтобы обеспечить более быстрое перечисление и более быстрые операции поиска, такие как Contains() или TryGetValue(). Эти коллекции оказываются особенно ценными в сценариях, где коллекции изначально заполняются и впоследствии сохраняются в течение всего жизненного цикла долгоживущего приложения.
См. подробнее о замороженных коллекциях
2) Класс System.Buffers.SearchValues<T> предназначен для эффективного поиска набора байтов или символов в другом наборе, например, в реализации String.IndexOfAny(char[]). При создании экземпляра SearchValues<T> все данные, необходимые для оптимизации будущего поиска, вычисляются заранее и выбирается наиболее эффективный алгоритм поиска. Класс уже широко используется внутри .NET, например, в кодогенерации для регулярных выражений.
3) Класс System.Text.CompositeFormat предназначен для оптимизации строк формата, таких как «Имя: {0} Фамилия: {1}», неизвестных во время компиляции. Он позволяет динамически создавать строки формата путем объединения строк и элементов формата (аналогично классу StringBuilder), а также предоставляет ряд функций для оптимизации строк формата, таких как кэширование и повторное использование. Несмотря на то, что такие задачи, как анализ строк, требуют начальных накладных расходов, этот упреждающий подход значительно снижает вычислительную нагрузку при последующих использованиях, повышая производительность и эффективность.
9. Поддержка набора инструкций Intel AVX-512.
В .NET Core 3.0 предложена поддержка SIMD за счёт включения аппаратной поддержки для платформы x86/x64. .NET 5 расширил эту поддержку до Arm64, в .NET 7 были представлены кроссплатформенные аппаратные функции. .NET 8 ещё расширяет возможности SIMD за счет введения Vector512<T> и поддержки инструкций Intel Advanced Vector Extensions 512 (AVX-512). Обратите внимание: если у вас есть оборудование, совместимое с AVX-512, Vector512.IsHardwareAccelerated теперь будет возвращать true.
10. Криптография
.NET 8 предлагает поддержку примитивов хеширования SHA-3. SHA-3 в настоящее время совместим с Linux и OpenSSL 1.1.1 или новее, а также с Windows 11 сборки 25324 или новее. Существующие API, предлагающие SHA-2, теперь включают свои аналоги SHA-3, включающие SHA3_256, SHA3_384 и SHA3_512 для хеширования; HashAlgorithmName.SHA3_256, HashAlgorithmName.SHA3_384 и HashAlgorithmName.SHA3_512 для хеширования, с настраиваемым алгоритмом; HMACSHA3_256, HMACSHA3_384 и HMACSHA3_512 для HMAC; и RSAEncryptionPadding.OaepSHA3_256, RSAEncryptionPadding.OaepSHA3_384 и RSAEncryptionPadding.OaepSHA3_512 для шифрования RSA OAEP.
Источник: https://blog.ndepend.com/net-8-top-10-new-features/
Топ 10 Новых Функций .NET 8. Окончание
Начало
Продолжение
7. Потоковые методы ZipFile
Теперь можно сжимать файлы из каталога в поток без необходимости кэшировать их во временный файл. Это позволяет напрямую управлять результатом сжатия в памяти. Новый API полезен в сценариях, где дисковое пространство ограничено, поскольку устраняет необходимость использования диска в качестве промежуточного шага. Новые статические методы: ZipFile.CreateFromDirectory и ExtractToDirectory.
8. Новые типы, которые могут улучшить производительность в различных ситуациях
1) System.Collections.Frozen содержит новые классы коллекций FrozenSet<T> и FrozenDictionary<TKey,TValue>. Эти коллекции нельзя изменить после создания. Внутри реализация использует это требование, чтобы обеспечить более быстрое перечисление и более быстрые операции поиска, такие как Contains() или TryGetValue(). Эти коллекции оказываются особенно ценными в сценариях, где коллекции изначально заполняются и впоследствии сохраняются в течение всего жизненного цикла долгоживущего приложения.
См. подробнее о замороженных коллекциях
2) Класс System.Buffers.SearchValues<T> предназначен для эффективного поиска набора байтов или символов в другом наборе, например, в реализации String.IndexOfAny(char[]). При создании экземпляра SearchValues<T> все данные, необходимые для оптимизации будущего поиска, вычисляются заранее и выбирается наиболее эффективный алгоритм поиска. Класс уже широко используется внутри .NET, например, в кодогенерации для регулярных выражений.
3) Класс System.Text.CompositeFormat предназначен для оптимизации строк формата, таких как «Имя: {0} Фамилия: {1}», неизвестных во время компиляции. Он позволяет динамически создавать строки формата путем объединения строк и элементов формата (аналогично классу StringBuilder), а также предоставляет ряд функций для оптимизации строк формата, таких как кэширование и повторное использование. Несмотря на то, что такие задачи, как анализ строк, требуют начальных накладных расходов, этот упреждающий подход значительно снижает вычислительную нагрузку при последующих использованиях, повышая производительность и эффективность.
9. Поддержка набора инструкций Intel AVX-512.
В .NET Core 3.0 предложена поддержка SIMD за счёт включения аппаратной поддержки для платформы x86/x64. .NET 5 расширил эту поддержку до Arm64, в .NET 7 были представлены кроссплатформенные аппаратные функции. .NET 8 ещё расширяет возможности SIMD за счет введения Vector512<T> и поддержки инструкций Intel Advanced Vector Extensions 512 (AVX-512). Обратите внимание: если у вас есть оборудование, совместимое с AVX-512, Vector512.IsHardwareAccelerated теперь будет возвращать true.
10. Криптография
.NET 8 предлагает поддержку примитивов хеширования SHA-3. SHA-3 в настоящее время совместим с Linux и OpenSSL 1.1.1 или новее, а также с Windows 11 сборки 25324 или новее. Существующие API, предлагающие SHA-2, теперь включают свои аналоги SHA-3, включающие SHA3_256, SHA3_384 и SHA3_512 для хеширования; HashAlgorithmName.SHA3_256, HashAlgorithmName.SHA3_384 и HashAlgorithmName.SHA3_512 для хеширования, с настраиваемым алгоритмом; HMACSHA3_256, HMACSHA3_384 и HMACSHA3_512 для HMAC; и RSAEncryptionPadding.OaepSHA3_256, RSAEncryptionPadding.OaepSHA3_384 и RSAEncryptionPadding.OaepSHA3_512 для шифрования RSA OAEP.
Источник: https://blog.ndepend.com/net-8-top-10-new-features/
👍15
День 1753. #Оффтоп
Давненько не рекомендовал вам видосиков. Сегодня просто полный оффтоп, и не совсем про программирование. У АйтиБороды вышло шикарное интервью с Интервью с Михаилом Климарёвым, директором Общества Защиты Интернета.
Как зарождался и развивался Рунет, как его пытаются контролировать сейчас. А поскольку я в сети тоже уже очень давно, то знатно поностальгировал по временам диалапа и ADSL.
Так что запасайтесь 3,5 часами свободного времени https://youtu.be/eaz3vSihX0o
ЗЫ: если у кого аллергия на политоту, то её тут довольно много. Если что, я предупредил.
Давненько не рекомендовал вам видосиков. Сегодня просто полный оффтоп, и не совсем про программирование. У АйтиБороды вышло шикарное интервью с Интервью с Михаилом Климарёвым, директором Общества Защиты Интернета.
Как зарождался и развивался Рунет, как его пытаются контролировать сейчас. А поскольку я в сети тоже уже очень давно, то знатно поностальгировал по временам диалапа и ADSL.
Так что запасайтесь 3,5 часами свободного времени https://youtu.be/eaz3vSihX0o
ЗЫ: если у кого аллергия на политоту, то её тут довольно много. Если что, я предупредил.
YouTube
Как убивают Рунет / Запреты и блокировки глазами айтишника / Интервью с Михаилом Климарёвым
Всё ещё кодишь без AI? Го в клуб "Эволюция Кода 🧬", научу: https://itbeard.com/evocoders
Сегодня будем говорить о том, как зарождался Рунет и когда власти начали его портить, как сегодня Рунет приближают к "Чебурнету", как технически работают блокировки…
Сегодня будем говорить о том, как зарождался Рунет и когда власти начали его портить, как сегодня Рунет приближают к "Чебурнету", как технически работают блокировки…
👎15👍11
День 1754. #Книги
Вчера пришла очередная книга, над переводом которой я работал совместно с сообществом DotNetRu. Крис Сейнти «Blazor в действии» — М.: ДМК Пресс, 2023.
Книгу у меня получилось не только вычитать, но и проверить код (кстати, исправил несколько косяков в оригинале). В принципе, тем, кто хочет познакомиться с технологией и понять, что там к чему и зачем, могу книгу порекомендовать. Единственная проблема, как и со всеми книгами по новым технологиям – книги сильно отстают, а новая технология быстро и кардинально меняется. Так и тут, после выхода .NET 8 различия между Blazor Server и Blazor WebAssembly практически нивелировались. Теперь при создании проекта не обязательно выбирать одно или другое, их можно использовать совместно. Однако, это не значит, что книга совсем бесполезна, вполне можно изучить основы по ней, а новинки посмотреть в документации (тем более, что эти нововведения только-только появились и пока ещё нет большой практики их применения).
Подробнее про книгу в статье на Хабре
Вчера пришла очередная книга, над переводом которой я работал совместно с сообществом DotNetRu. Крис Сейнти «Blazor в действии» — М.: ДМК Пресс, 2023.
Книгу у меня получилось не только вычитать, но и проверить код (кстати, исправил несколько косяков в оригинале). В принципе, тем, кто хочет познакомиться с технологией и понять, что там к чему и зачем, могу книгу порекомендовать. Единственная проблема, как и со всеми книгами по новым технологиям – книги сильно отстают, а новая технология быстро и кардинально меняется. Так и тут, после выхода .NET 8 различия между Blazor Server и Blazor WebAssembly практически нивелировались. Теперь при создании проекта не обязательно выбирать одно или другое, их можно использовать совместно. Однако, это не значит, что книга совсем бесполезна, вполне можно изучить основы по ней, а новинки посмотреть в документации (тем более, что эти нововведения только-только появились и пока ещё нет большой практики их применения).
Подробнее про книгу в статье на Хабре
👍33
День 1755. #ЧтоНовенького
Упрощаем Облачную Разработку с .NET Aspire
.NET Aspire — это оркестратор для создания отказоустойчивых, наблюдаемых и настраиваемых облачных приложений в .NET. Он включает набор компонентов для обнаружения сервисов, телеметрии, поддержки устойчивости и проверки работоспособности.
Новый шаблон .NET Aspire Starter доступен в Visual Studio 2022 (17.9 Preview 1), либо из командной строки. Приложение шаблона состоит из двух проектов (веб-интерфейс Blazor и API) и кэша Redis. Также включены два новых проекта:
1.
2.
Панель мониторинга
Запуск приложения .NET Aspire отображает панель мониторинга. Она даёт единое представление о ваших сервисах с их журналами, метриками, трассировками и ошибками. На картинке 1 ниже показано изображение проекта с ошибкой, обозначенной красной точкой. Также можно смотреть журналы всех проектов и распределённую трассировку, показывающую запрос на страницу погоды (см. картинку 2 ниже). На панели собираются все диагностические данные во время разработки. Используются открытые стандарты, такие как Grafana+Prometheus, Application Insights и т. д.
Компоненты
В веб-проекте приложения есть NuGet-пакет Aspire.StackExchange.Redis.OutputCaching. Такие пакеты называются компонентами .NET Aspire. Это связующие библиотеки, которые настраивают SDK для работы в облачной среде. Каждый компонент должен:
- Предоставлять схему JSON для конфигурации.
- Использовать настраиваемые шаблоны устойчивости, такие как повторные попытки, тайм-ауты и автоматические выключатели, чтобы максимизировать доступность.
- Предоставлять проверки работоспособности.
- Предлагать интегрированное ведение журнала, метрики и трассировку с использованием современных абстракций .NET (ILogger, Meter, Activity).
- Предлагать методы расширения, которые «склеивают» сервисы из SDK в DI-контейнер с правильным временем жизни для регистрируемых типов.
То есть компоненты .NET Aspire настраивают зависимости для соблюдения ряда требований для успешной работы в облаке. Они не оборачивают и не скрывают фактический SDK/библиотеку, а действуют как связующее звено, гарантируя, что библиотека настроена с хорошим набором значений по умолчанию и правильно зарегистрирована в DI.
Код
В Program.cs веб-проекта вы можете увидеть код:
Это настройка веб-интерфейса для вызова API погоды. Название apiservice взято из проекта AppHost:
AppHost —стартовый проект. Он запускает все проекты, их зависимости и настраивает их, позволяя им взаимодействовать. Механизм обнаружения сервисов позволяет использовать логические имена вместо IP-адресов и портов при выполнении HTTP-вызовов. Здесь API-сервис назван "apiservice", и это имя можно использовать при выполнении HTTP-вызовов через IHttpClientFactory. Аналогично кэш Redis назван "cache". Вызовы, выполненные с использованием этого метода, также будут автоматически повторяться и обрабатывать временные сбои благодаря интеграции с Polly.
Подробнее о .NET Aspire смотрите в:
- докладе с .NET Conf
- видео от Ника Чапсаса
- деплой в Azure от Ника Чапсаса
Источник: https://devblogs.microsoft.com/dotnet/introducing-dotnet-aspire-simplifying-cloud-native-development-with-dotnet-8/
Упрощаем Облачную Разработку с .NET Aspire
.NET Aspire — это оркестратор для создания отказоустойчивых, наблюдаемых и настраиваемых облачных приложений в .NET. Он включает набор компонентов для обнаружения сервисов, телеметрии, поддержки устойчивости и проверки работоспособности.
Новый шаблон .NET Aspire Starter доступен в Visual Studio 2022 (17.9 Preview 1), либо из командной строки. Приложение шаблона состоит из двух проектов (веб-интерфейс Blazor и API) и кэша Redis. Также включены два новых проекта:
1.
<appname>.AppHost
запускает .NET-проекты, контейнеры или исполняемые файлы, необходимые для вашего распределённого приложения.2.
<appname>.ServiceDefaults
содержит общую сервисную логику, которая применяется к каждому из проектов в приложении: обнаружение сервисов, телеметрия и конечные точки проверки работоспособности.Панель мониторинга
Запуск приложения .NET Aspire отображает панель мониторинга. Она даёт единое представление о ваших сервисах с их журналами, метриками, трассировками и ошибками. На картинке 1 ниже показано изображение проекта с ошибкой, обозначенной красной точкой. Также можно смотреть журналы всех проектов и распределённую трассировку, показывающую запрос на страницу погоды (см. картинку 2 ниже). На панели собираются все диагностические данные во время разработки. Используются открытые стандарты, такие как Grafana+Prometheus, Application Insights и т. д.
Компоненты
В веб-проекте приложения есть NuGet-пакет Aspire.StackExchange.Redis.OutputCaching. Такие пакеты называются компонентами .NET Aspire. Это связующие библиотеки, которые настраивают SDK для работы в облачной среде. Каждый компонент должен:
- Предоставлять схему JSON для конфигурации.
- Использовать настраиваемые шаблоны устойчивости, такие как повторные попытки, тайм-ауты и автоматические выключатели, чтобы максимизировать доступность.
- Предоставлять проверки работоспособности.
- Предлагать интегрированное ведение журнала, метрики и трассировку с использованием современных абстракций .NET (ILogger, Meter, Activity).
- Предлагать методы расширения, которые «склеивают» сервисы из SDK в DI-контейнер с правильным временем жизни для регистрируемых типов.
То есть компоненты .NET Aspire настраивают зависимости для соблюдения ряда требований для успешной работы в облаке. Они не оборачивают и не скрывают фактический SDK/библиотеку, а действуют как связующее звено, гарантируя, что библиотека настроена с хорошим набором значений по умолчанию и правильно зарегистрирована в DI.
Код
В Program.cs веб-проекта вы можете увидеть код:
builder.Services
.AddHttpClient<WeatherApiClient>(
client => client
.BaseAddress = new("https://apiservice"));
Это настройка веб-интерфейса для вызова API погоды. Название apiservice взято из проекта AppHost:
var builder =
DistributedApplication.CreateBuilder(args);
var cache =
builder.AddRedisContainer("cache");
var apiservice = builder
.AddProject<Projects.AspireApp_ApiService>(
"apiservice");
Builder
.AddProject<Projects.AspireApp_Web>("webfrontend")
.WithReference(cache)
.WithReference(apiservice);
builder.Build().Run();
AppHost —стартовый проект. Он запускает все проекты, их зависимости и настраивает их, позволяя им взаимодействовать. Механизм обнаружения сервисов позволяет использовать логические имена вместо IP-адресов и портов при выполнении HTTP-вызовов. Здесь API-сервис назван "apiservice", и это имя можно использовать при выполнении HTTP-вызовов через IHttpClientFactory. Аналогично кэш Redis назван "cache". Вызовы, выполненные с использованием этого метода, также будут автоматически повторяться и обрабатывать временные сбои благодаря интеграции с Polly.
Подробнее о .NET Aspire смотрите в:
- докладе с .NET Conf
- видео от Ника Чапсаса
- деплой в Azure от Ника Чапсаса
Источник: https://devblogs.microsoft.com/dotnet/introducing-dotnet-aspire-simplifying-cloud-native-development-with-dotnet-8/
👍12
День 1756. #Testing
Тестируем Валидацию Модели
Валидация модели имеет основополагающее значение для любого проекта: она обеспечивает безопасность и надёжность, выступая в качестве защиты от недопустимого состояния модели.
Надо бы добавить модульные тесты валидации модели. Фактически, при определении входной модели всегда следует учитывать как допустимые, так и, тем более, недопустимые модели, следя за тем, чтобы все недопустимые модели отбрасывались.
Определим простую модель:
У нас есть два варианта: мы можем написать интеграционные тесты для отправки запросов в систему, на которой работает сервер, и проверить полученный ответ. Или мы можем использовать внутренний класс Validator, который используется ASP.NET для проверки моделей ввода, и создать быстрые юнит-тесты. Вот вспомогательный метод, который мы можем использовать в тестах:
Мы создаём контекст проверки без какой-либо внешней зависимости, ориентированный только на модель ввода. Затем мы проверяем все свойства, вызывая TryValidateObject, и сохраняем результаты проверки в списке results. Наконец, если модель реализует интерфейс IValidatableObject, который предоставляет метод Validate, мы вызываем его и добавляем возвращённые ошибки в тот же список results. Таким образом мы можем обрабатывать как атрибуты полей, такие как [Required], так и пользовательскую проверку в методе Validate() класса модели.
Используем этот метод в тестах:
Аналогично мы можем проверять и правильность возвращаемых ошибок, которые можно кастомизировать как в атрибутах валидации (свойство ErrorMessage), так и в методе Validate().
Источник: https://www.code4it.dev/csharptips/unit-test-model-validation/
Тестируем Валидацию Модели
Валидация модели имеет основополагающее значение для любого проекта: она обеспечивает безопасность и надёжность, выступая в качестве защиты от недопустимого состояния модели.
Надо бы добавить модульные тесты валидации модели. Фактически, при определении входной модели всегда следует учитывать как допустимые, так и, тем более, недопустимые модели, следя за тем, чтобы все недопустимые модели отбрасывались.
Определим простую модель:
public class User
{
[Required]
[MinLength(3)]
public string FirstName { get; set; }
[Required]
[MinLength(3)]
public string LastName { get; set; }
[Range(18, 100)]
public int Age { get; set; }
}
У нас есть два варианта: мы можем написать интеграционные тесты для отправки запросов в систему, на которой работает сервер, и проверить полученный ответ. Или мы можем использовать внутренний класс Validator, который используется ASP.NET для проверки моделей ввода, и создать быстрые юнит-тесты. Вот вспомогательный метод, который мы можем использовать в тестах:
public static IList<ValidationResult>
ValidateModel(object model)
{
var results = new List<ValidationResult>();
var context =
new ValidationContext(model, null, null);
Validator.TryValidateObject(
model, context, results, true);
if (model is IValidatableObject vm)
results.AddRange(vm.Validate(context));
return results;
}
Мы создаём контекст проверки без какой-либо внешней зависимости, ориентированный только на модель ввода. Затем мы проверяем все свойства, вызывая TryValidateObject, и сохраняем результаты проверки в списке results. Наконец, если модель реализует интерфейс IValidatableObject, который предоставляет метод Validate, мы вызываем его и добавляем возвращённые ошибки в тот же список results. Таким образом мы можем обрабатывать как атрибуты полей, такие как [Required], так и пользовательскую проверку в методе Validate() класса модели.
Используем этот метод в тестах:
[Test]
public void Pass_WhenModelValid()
{
var model = new User {
FirstName = "Jon",
LastName = "Smith",
Age = 42
};
var result = ValidateModel(model);
Assert.That(result, Is.Empty);
}
[Test]
public void Fail_WhenAgeLessThan18()
{
var model = new User {
FirstName = "Jane",
LastName = "Smith",
Age = 17
};
var result = ValidateModel(model);
Assert.That(result, Is.Not.Empty);
}
Аналогично мы можем проверять и правильность возвращаемых ошибок, которые можно кастомизировать как в атрибутах валидации (свойство ErrorMessage), так и в методе Validate().
Источник: https://www.code4it.dev/csharptips/unit-test-model-validation/
👍12
День 1757. #ЗаметкиНаПолях
Версионирование API. Начало
Версионирование используется для управления изменениями в API при сохранении обратной совместимости для существующих клиентов. Это позволяет разработчикам внедрять новые функции, исправлять ошибки или вносить другие изменения в API, не влияя на функциональность существующих интеграций.
Причины:
- Обратная совместимость: гарантирует, что клиенты смогут продолжать использовать старую версию, в то время как новые клиенты смогут воспользоваться преимуществами обновлённых функций.
- Эволюция API: позволяет внедрять новые функции, отказываться от устаревших функций и вносить улучшения, не нарушая работу существующих клиентов.
- Гибкость для клиента: у разных клиентов могут быть разные требования или требоваться определённые функции, доступные в конкретной версии API.
Начиная с .NET 5, есть 5 вариантов версионирования API.
1. По URI (Пути)
Версия включается непосредственно в URL (
+ Наиболее простой и понятный метод.
- Может привести к дублированию URL и потребует от клиентов изменять URL для доступа к различным версиям.
2. По строке запроса
Версия указывается в качестве параметра запроса в URL (
+ Позволяет легко перейти на новую версию.
- Может быть не так очевидно, как управление через путь, и пользователи могут не заметить параметра.
Окончание следует…
Источник: https://stefandjokic.tech/blog
Версионирование API. Начало
Версионирование используется для управления изменениями в API при сохранении обратной совместимости для существующих клиентов. Это позволяет разработчикам внедрять новые функции, исправлять ошибки или вносить другие изменения в API, не влияя на функциональность существующих интеграций.
Причины:
- Обратная совместимость: гарантирует, что клиенты смогут продолжать использовать старую версию, в то время как новые клиенты смогут воспользоваться преимуществами обновлённых функций.
- Эволюция API: позволяет внедрять новые функции, отказываться от устаревших функций и вносить улучшения, не нарушая работу существующих клиентов.
- Гибкость для клиента: у разных клиентов могут быть разные требования или требоваться определённые функции, доступные в конкретной версии API.
Начиная с .NET 5, есть 5 вариантов версионирования API.
1. По URI (Пути)
Версия включается непосредственно в URL (
/api/v1/products
).+ Наиболее простой и понятный метод.
- Может привести к дублированию URL и потребует от клиентов изменять URL для доступа к различным версиям.
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsV1Controller : ApiController
{
public IHttpActionResult Get()
{
// Реализация для версии 1
}
}
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsV2Controller : ApiController
{
public IHttpActionResult Get()
{
// Реализация для версии 2
}
}
2. По строке запроса
Версия указывается в качестве параметра запроса в URL (
/api/products?version=1
).+ Позволяет легко перейти на новую версию.
- Может быть не так очевидно, как управление через путь, и пользователи могут не заметить параметра.
public class ProductsController : ApiController
{
const string DefaultVersion = "1.0";
[HttpGet]
public IHttpActionResult Get()
{
var version = HttpContext.Current
.Request.QueryString["version"]
?? DefaultVersion;
if (version == "1.0")
{
// Реализация для версии 1
return Ok(…);
}
if (version == "2.0")
{
// Реализация для версии 2
return Ok(…);
}
return BadRequest("Версия не поддерживается");
}
}
Окончание следует…
Источник: https://stefandjokic.tech/blog
👍17
День 1758. #ЗаметкиНаПолях
Версионирование API. Окончание
Начало
3. По HTTP-заголовку
Информация о версии отправляется в виде пользовательского заголовка в HTTP-запросе (
+ URL остаётся неизменным для разных версий, что может быть полезно для SEO и кэширования.
- Версию труднее обнаружить и сложнее тестировать, поскольку требуется установка HTTP-заголовков.
4. По Media Type (заголовок Accept)
Информация о версии включается в заголовок Accept HTTP-запроса, часто с использованием пользовательского типа (
+ Метод точно соответствует спецификации HTTP.
- Более сложно, а некоторым клиентам API может быть сложно управлять версиями.
5. По имени хоста
Разные версии API размещаются на разных доменных именах (
+ Наиболее интуитивно понятен для конечных пользователей.
- Требует дополнительной настройки инфраструктуры и может усложнить управление сертификатами SSL.
Итого
У каждого метода есть свои компромиссы, и выбор того, какой из них использовать, будет зависеть от конкретных требований и ограничений проекта. Также можно комбинировать эти методы. Конечная цель в том, чтобы поддерживать надёжный контракт с потребителями API, обеспечивая при этом возможность дальнейшего развития и улучшения сервисов, предоставляемых через API.
Источник: https://stefandjokic.tech/blog
Версионирование API. Окончание
Начало
3. По HTTP-заголовку
Информация о версии отправляется в виде пользовательского заголовка в HTTP-запросе (
X-API-Version: 1
).+ URL остаётся неизменным для разных версий, что может быть полезно для SEO и кэширования.
- Версию труднее обнаружить и сложнее тестировать, поскольку требуется установка HTTP-заголовков.
public class ProductsController : ApiController
{
private const string HeaderName = "X-API-Version";
[HttpGet]
public IHttpActionResult Get()
{
var version = HttpContext.Current
.Request.Headers[HeaderName];
if (version == "1.0")
{
// Реализация для версии 1
return Ok(…);
}
if (version == "2.0")
{
// Реализация для версии 2
return Ok(…);
}
return BadRequest("Требуется версия API");
}
}
4. По Media Type (заголовок Accept)
Информация о версии включается в заголовок Accept HTTP-запроса, часто с использованием пользовательского типа (
application/vnd.myapi.v1+json
).+ Метод точно соответствует спецификации HTTP.
- Более сложно, а некоторым клиентам API может быть сложно управлять версиями.
[Produces("application/json")]
public class ProductsController : ApiController
{
[HttpGet]
public IHttpActionResult Get()
{
var version = GetVersionFromAcceptHeader();
// реализация аналогична методу 3
}
private string GetVersionFromAcceptHeader()
{
var header = Request.Headers.
Accept.FirstOrDefault();
if (header != null)
{
var version =
header.Parameters.FirstOrDefault(
p => p.Name.Equals("version",
StringComparison.OrdinalIgnoreCase));
return version?.Value;
}
return null;
}
}
5. По имени хоста
Разные версии API размещаются на разных доменных именах (
api-v1.example.com
). Может осуществляться через маршрутизацию или перезапись URL. Обычно не обрабатывается непосредственно в контроллере, а скорее в настройках IIS или обратного прокси.+ Наиболее интуитивно понятен для конечных пользователей.
- Требует дополнительной настройки инфраструктуры и может усложнить управление сертификатами SSL.
Итого
У каждого метода есть свои компромиссы, и выбор того, какой из них использовать, будет зависеть от конкретных требований и ограничений проекта. Также можно комбинировать эти методы. Конечная цель в том, чтобы поддерживать надёжный контракт с потребителями API, обеспечивая при этом возможность дальнейшего развития и улучшения сервисов, предоставляемых через API.
Источник: https://stefandjokic.tech/blog
👍11
День 1759. #Оффтоп #Безопасность
Осторожно: URL – Это Указатели на Изменяемые Сущности
Люди часто оценивают URL как: «Безопасные или вредоносные». В частности, поставщики продуктов безопасности ставят URL в один ряд с такими индикаторами опасности, как хэш известного вредоносного файла, вредоносный/скомпрометированный цифровой сертификат или заведомо вредоносный IP.
К сожалению, это разные вещи. Хэш файла никогда не меняется, содержимое файла не может измениться без изменения его хэша, и «безвредный» файл не может стать «вредоносным»*.
*Мы говорим о самом файле, а не о пути к файлу.
Однако IP или URL, может легко измениться с «безобидного» на «опасный» и наоборот. IP – просто указатель, а URL — указатель на указатель. И даже после того, как клиент подключается к целевому серверу, этот сервер решает, как интерпретировать запрос, и может каждый раз возвращать разное содержимое.
Поэтому не только URL со временем может измениться с безобидного на вредоносный (например, злоумышленник приобретёт домен после истечения срока его регистрации). URL может даже быть одновременно безвредным и вредоносным в зависимости от того, кто его запрашивает (например, злоумышленник может «замаскировать» свою атаку, чтобы вернуть вредоносный контент целевым жертвам, одновременно предоставляя безвредный контент другим). Или сервер может выдавать разные ответы разным клиентам.
Недавно поиск «YouTube» в Google приводил к тому, что первая ссылка на странице была «рекламной» ссылкой. Если ничего не подозревающий пользователь нажимал на ссылку, он попадал на сайт мошенников, которые пытались захватить экран и убедить пользователя, что ему нужно позвонить злоумышленнику.
Как это возможно? Злоумышленники просто воспользовались тем фактом, что URL являются указателями на изменяемые объекты. Скорее всего злоумышленник разместил заказ на рекламу и указал ссылку на редиректор, который перенаправлял на какую-то страницу на YouTube. Система проверки рекламы Google проверила целевой URL, чтобы убедиться, что он действительно указывает на YouTube, и начала показывать рекламу. Затем злоумышленник обновил редиректор на свой мошеннический сайт — классическая уязвимость «время проверки, время использования».
Цепочка перенаправлений браузера при нажатии ссылки
Из-за того, как работает модель безопасности веб-платформы, способность Google обнаруживать такого рода уловки ограничена: после того, как браузер пользователя покидает сервер googleadservices.com, рекламная система Google не знает, где в итоге окажется пользователь, и не может знать, что следующий редиректор теперь отправляет пользователя на сайт атаки.
Более того, вы можете подсмотреть на подсказку статуса браузера в нижнем углу браузера, чтобы увидеть, куда ведёт ссылка, прежде чем нажать на неё. Но на самом деле перед этим есть ещё один уровень косвенности: ссылка (элемент
В частности, страница результатов поиска Google помещает URL в тэг
Вредоносные сайты всегда могли подделать статус, но производители браузеров ожидали, что «хорошие сайты» этого не делают. К сожалению, это неверное предположение. В этом случае отображение «настоящего» URL в статусе, вероятно, не добавит никакой защиты для осторожного пользователя — все ссылки на странице результатов Google проходят через какой-то редиректор.
Источник: https://textslashplain.com/2023/10/13/beware-urls-are-pointers-to-mutable-entities/
Осторожно: URL – Это Указатели на Изменяемые Сущности
Люди часто оценивают URL как: «Безопасные или вредоносные». В частности, поставщики продуктов безопасности ставят URL в один ряд с такими индикаторами опасности, как хэш известного вредоносного файла, вредоносный/скомпрометированный цифровой сертификат или заведомо вредоносный IP.
К сожалению, это разные вещи. Хэш файла никогда не меняется, содержимое файла не может измениться без изменения его хэша, и «безвредный» файл не может стать «вредоносным»*.
*Мы говорим о самом файле, а не о пути к файлу.
Однако IP или URL, может легко измениться с «безобидного» на «опасный» и наоборот. IP – просто указатель, а URL — указатель на указатель. И даже после того, как клиент подключается к целевому серверу, этот сервер решает, как интерпретировать запрос, и может каждый раз возвращать разное содержимое.
Поэтому не только URL со временем может измениться с безобидного на вредоносный (например, злоумышленник приобретёт домен после истечения срока его регистрации). URL может даже быть одновременно безвредным и вредоносным в зависимости от того, кто его запрашивает (например, злоумышленник может «замаскировать» свою атаку, чтобы вернуть вредоносный контент целевым жертвам, одновременно предоставляя безвредный контент другим). Или сервер может выдавать разные ответы разным клиентам.
Недавно поиск «YouTube» в Google приводил к тому, что первая ссылка на странице была «рекламной» ссылкой. Если ничего не подозревающий пользователь нажимал на ссылку, он попадал на сайт мошенников, которые пытались захватить экран и убедить пользователя, что ему нужно позвонить злоумышленнику.
Как это возможно? Злоумышленники просто воспользовались тем фактом, что URL являются указателями на изменяемые объекты. Скорее всего злоумышленник разместил заказ на рекламу и указал ссылку на редиректор, который перенаправлял на какую-то страницу на YouTube. Система проверки рекламы Google проверила целевой URL, чтобы убедиться, что он действительно указывает на YouTube, и начала показывать рекламу. Затем злоумышленник обновил редиректор на свой мошеннический сайт — классическая уязвимость «время проверки, время использования».
Цепочка перенаправлений браузера при нажатии ссылки
Из-за того, как работает модель безопасности веб-платформы, способность Google обнаруживать такого рода уловки ограничена: после того, как браузер пользователя покидает сервер googleadservices.com, рекламная система Google не знает, где в итоге окажется пользователь, и не может знать, что следующий редиректор теперь отправляет пользователя на сайт атаки.
Более того, вы можете подсмотреть на подсказку статуса браузера в нижнем углу браузера, чтобы увидеть, куда ведёт ссылка, прежде чем нажать на неё. Но на самом деле перед этим есть ещё один уровень косвенности: ссылка (элемент
<a>
) сама по себе является указателем-на-указатель-на-указатель. Благодаря манипуляциям с JavaScript URL, на который указывает ссылка на странице, может измениться при нажатии на него.В частности, страница результатов поиска Google помещает URL в тэг
<a>
, но, когда пользователь нажимает на него, URL изменяется на «настоящий»: googleadservices.com/...
и только потом осуществляется перенаправление на целевой URL.Вредоносные сайты всегда могли подделать статус, но производители браузеров ожидали, что «хорошие сайты» этого не делают. К сожалению, это неверное предположение. В этом случае отображение «настоящего» URL в статусе, вероятно, не добавит никакой защиты для осторожного пользователя — все ссылки на странице результатов Google проходят через какой-то редиректор.
Источник: https://textslashplain.com/2023/10/13/beware-urls-are-pointers-to-mutable-entities/
👍10
День 1760. #ЗаметкиНаПолях
Меняем Поведение Конструкторов Копирования Записей
Когда вы используете записи, вы можете создать новый экземпляр, используя ключевое слово new или скопировать экземпляр с некоторыми изменениями, используя выражение with. Выражение with копирует все поля из исходного экземпляра, а затем применяет изменения.
Если запись содержит изменяемый объект, например List<T>, экземпляр будет совместно использоваться оригиналом и копией. Это связано с тем, что выражение with не клонирует изменяемый объект:
При использовании выражения with вызывается созданный компилятором метод <Clone>$. Он вызывает конструктор копирования. Если вы не реализуете конструктор копирования, компилятор сгенерирует конструктор, копирующий все поля. Вот сгенерированный код для записи Person:
Если вы хотите клонировать свойство Phones при использовании выражения with, вы можете реализовать конструктор копирования вручную:
Источник: https://www.meziantou.net/customizing-the-behavior-of-the-record-copy-constructor.htm
Меняем Поведение Конструкторов Копирования Записей
Когда вы используете записи, вы можете создать новый экземпляр, используя ключевое слово new или скопировать экземпляр с некоторыми изменениями, используя выражение with. Выражение with копирует все поля из исходного экземпляра, а затем применяет изменения.
var john = new Person("John", "Doe");
var Jane = john with { FirstName = "Jane" };
record Person(string FirstName, string LastName);
Если запись содержит изменяемый объект, например List<T>, экземпляр будет совместно использоваться оригиналом и копией. Это связано с тем, что выражение with не клонирует изменяемый объект:
var john = new Person("John", "Doe") {
Phones = ["1234567890"]
};
var jane = john with { FirstName = "Jane" };
jane.Phones.Add("123");
// Both list are modified
Console.WriteLine(john.Phones.Count); // 2
Console.WriteLine(jane.Phones.Count); // 2
public record Person(string FirstName, string LastName)
{
public List<string>? Phones { get; init; }
}
При использовании выражения with вызывается созданный компилятором метод <Clone>$. Он вызывает конструктор копирования. Если вы не реализуете конструктор копирования, компилятор сгенерирует конструктор, копирующий все поля. Вот сгенерированный код для записи Person:
internal class Person
{
// ...
[CompilerGenerated]
public virtual Person <Clone>$()
{
return new Person(this);
}
[CompilerGenerated]
protected Person(Person original)
{
<FirstName>k__BackingField = original.<FirstName>k__BackingField;
<LastName>k__BackingField = original.<LastName>k__BackingField;
<Phones>k__BackingField = original.<Phones>k__BackingField;
}
}
Если вы хотите клонировать свойство Phones при использовании выражения with, вы можете реализовать конструктор копирования вручную:
public record Person(string FirstName, string LastName)
{
public List<string>? Phones { get; init; }
protected Person(Person other)
{
FirstName = other.FirstName;
LastName = other.LastName;
if(other.Phones != null)
Phones = new List<string>(other.Phones);
}
}
var john = new Person("John", "Doe") {
Phones = ["1234567890"];
var jane = john with { FirstName = "Jane" };
jane.Phones.Add("0987654321");
// Изменится только оригинал, но не копия
Console.WriteLine(john.Phones.Count); // 1
Console.WriteLine(jane.Phones.Count); // 2
Источник: https://www.meziantou.net/customizing-the-behavior-of-the-record-copy-constructor.htm
👍23👎2
День 1761. #Docs
Пишем Документацию, Которую Будут Читать и Использовать
Вы завершили работу и выпустили классный продукт. Но никто, кроме вас, не знает, как им пользоваться. Вы начинаете это описывать… но не помните, как решили ту проблему полгода назад. Многое из того, что вы пишете, предполагает, что читатель знает столько же, сколько вы. И вы даже не помните, как некоторые детали соединены или как должны работать вместе. Это очень распространённый сценарий. Документация часто пишется после того, как всё сделано, и с учётом времени до дедлайна. Мы забываем важные шаги и предполагаем, что у читателей есть опыт и знания. Это делает документацию трудной для чтения и разочаровывает читателей.
Что это?
Мы будем рассматривать документацию как письменное описание всего, над чем вы работали, с инструкциями по использованию готового продукта.
Почему это важно?
Хорошая документация будет служить для читателя путеводителем по продукту, поможет понять, что это, зачем и как работает. Без хорошей документации люди, скорее всего, не смогут полноценно (или вообще) использовать продукт. По мере увеличения сложности продукта качество документации становится всё более важным.
Когда писать?
Часто её пишут в конце. Но это, наверное, худшее время. К тому времени, вероятно, что вы уже начнёте забывать некоторые детали. Документируйте в процессе, шаг за шагом. Держите документацию открытой и близко, чтобы записывать все новые идеи. В конце используйте оставшееся время для редактирования документации.
Как написать хорошую документацию?
Хорошая документация не разочаровывает отсутствием важных шагов, не предполагает наличия опыта, читается легко, содержит чёткие и воспроизводимые шаги и приводит к выполнению нужной задачи.
1. Пишите для своей аудитории
Переоценка или недооценка аудитории, скорее всего, затруднит использование документации для ваших реальных читателей. Предположения о знаниях людей могут оказаться исключающими, если вы не будете осторожны.
Например, вы документируете пакет для тестирования сайта. Если вы предполагаете, что ваша аудитория знает систему, можно начать с «Шаг 1. Запустите веб-сайт локально». Это предположение исключает из аудитории любого, кто не знает, как это сделать. Остальное написанное может быть для него бесполезным.
Другая крайность — допущение, что аудитория ничего не знает. Если это опытные тестировщики, а документация читается так, будто написана для нетехнических людей, читатель может почувствовать, что вы разговариваете с ним свысока, заскучать и бросить чтение. Важен поиск золотой середины.
Пакет тестирования скорее всего используют тестировщики и разработчики. Если он хорош, им могут пользоваться долгие годы. Т.е. у кого-то может быть опыт, а кому-то нужно будет повышение квалификации. Как учесть и тех, и других?
2. Используйте модульную документацию
Возвращаясь к «Шаг 1. Запустите веб-сайт локально». Опытным читателям больше объяснять не надо. А для новичков… напишите больше документации! Точнее, посмотрите, существует ли уже документация для этого. Если да, и она актуальна, оставьте ссылку на неё. Если нет, напишите новую страницу документации об этом.
Модульность документации позволит обеим аудиториям продолжить работу на своём уровне. Экспертам - пройти этот шаг быстро, новичкам - копнуть глубже и получить новый опыт.
3. Пусть её проверят
В идеале проверку должен выполнять коллега, который знает систему, а также кто-то из потенциальной аудитории. Прекрасным примером является документация по онбордингу (вводная документация, позволяющая новичкам в компании быстро освоиться). Обычно её авторы не всегда помнят, что было необходимо и важно для новичка. Легко делать предположения и забыть важные детали. Поэтому её должен проверить тот, кто реально проходит этот процесс. Сядьте с ним и наблюдайте, как он использует документацию, какие трудности возникают. Добавляйте недостающие шаги или упущенные детали, пока документация не станет пригодной для использования без помощи.
Источник
Пишем Документацию, Которую Будут Читать и Использовать
Вы завершили работу и выпустили классный продукт. Но никто, кроме вас, не знает, как им пользоваться. Вы начинаете это описывать… но не помните, как решили ту проблему полгода назад. Многое из того, что вы пишете, предполагает, что читатель знает столько же, сколько вы. И вы даже не помните, как некоторые детали соединены или как должны работать вместе. Это очень распространённый сценарий. Документация часто пишется после того, как всё сделано, и с учётом времени до дедлайна. Мы забываем важные шаги и предполагаем, что у читателей есть опыт и знания. Это делает документацию трудной для чтения и разочаровывает читателей.
Что это?
Мы будем рассматривать документацию как письменное описание всего, над чем вы работали, с инструкциями по использованию готового продукта.
Почему это важно?
Хорошая документация будет служить для читателя путеводителем по продукту, поможет понять, что это, зачем и как работает. Без хорошей документации люди, скорее всего, не смогут полноценно (или вообще) использовать продукт. По мере увеличения сложности продукта качество документации становится всё более важным.
Когда писать?
Часто её пишут в конце. Но это, наверное, худшее время. К тому времени, вероятно, что вы уже начнёте забывать некоторые детали. Документируйте в процессе, шаг за шагом. Держите документацию открытой и близко, чтобы записывать все новые идеи. В конце используйте оставшееся время для редактирования документации.
Как написать хорошую документацию?
Хорошая документация не разочаровывает отсутствием важных шагов, не предполагает наличия опыта, читается легко, содержит чёткие и воспроизводимые шаги и приводит к выполнению нужной задачи.
1. Пишите для своей аудитории
Переоценка или недооценка аудитории, скорее всего, затруднит использование документации для ваших реальных читателей. Предположения о знаниях людей могут оказаться исключающими, если вы не будете осторожны.
Например, вы документируете пакет для тестирования сайта. Если вы предполагаете, что ваша аудитория знает систему, можно начать с «Шаг 1. Запустите веб-сайт локально». Это предположение исключает из аудитории любого, кто не знает, как это сделать. Остальное написанное может быть для него бесполезным.
Другая крайность — допущение, что аудитория ничего не знает. Если это опытные тестировщики, а документация читается так, будто написана для нетехнических людей, читатель может почувствовать, что вы разговариваете с ним свысока, заскучать и бросить чтение. Важен поиск золотой середины.
Пакет тестирования скорее всего используют тестировщики и разработчики. Если он хорош, им могут пользоваться долгие годы. Т.е. у кого-то может быть опыт, а кому-то нужно будет повышение квалификации. Как учесть и тех, и других?
2. Используйте модульную документацию
Возвращаясь к «Шаг 1. Запустите веб-сайт локально». Опытным читателям больше объяснять не надо. А для новичков… напишите больше документации! Точнее, посмотрите, существует ли уже документация для этого. Если да, и она актуальна, оставьте ссылку на неё. Если нет, напишите новую страницу документации об этом.
Модульность документации позволит обеим аудиториям продолжить работу на своём уровне. Экспертам - пройти этот шаг быстро, новичкам - копнуть глубже и получить новый опыт.
3. Пусть её проверят
В идеале проверку должен выполнять коллега, который знает систему, а также кто-то из потенциальной аудитории. Прекрасным примером является документация по онбордингу (вводная документация, позволяющая новичкам в компании быстро освоиться). Обычно её авторы не всегда помнят, что было необходимо и важно для новичка. Легко делать предположения и забыть важные детали. Поэтому её должен проверить тот, кто реально проходит этот процесс. Сядьте с ним и наблюдайте, как он использует документацию, какие трудности возникают. Добавляйте недостающие шаги или упущенные детали, пока документация не станет пригодной для использования без помощи.
Источник
👍7👎2
День 1762. #ЗаметкиНаПолях
Повышаем Производительность EF Core с Помощью Скомпилированных Запросов
Скомпилированные запросы в .NET появились как ответ на проблемы с производительностью, возникающие при операциях извлечения данных, особенно в приложениях, использующих ORM, такие как Entity Framework.
До их появления каждая операция извлечения данных требовала от ORM преобразования LINQ в запросы SQL, а этот процесс занимал много времени и ресурсов.
Допустим, у нас есть класс модели данных:
Стандартный код для извлечения пользователя по Id будет примерно таким:
Создадим скомпилированный запрос:
Здесь:
- Статическое поле getUser типа Func<AppDbContext, int, User> - это скомпилированный запрос с использованием метода EF.CompileQuery.
- Метод GetUser – принимает id в качестве параметра и использует статическое поле getUser для получения объекта User из БД.
Мы также можем использовать асинхронный скомпилированный запрос с помощью EF.CompileAsyncQuery. Тогда поле должно быть типа Func<AppDbContext, int, Task<User>>, а метод GetUser будет асинхронным:
Теперь можно вызвать метод, созданный в нашем контексте:
Зачем использовать скомпилированные запросы?
1. Производительность
Преобразование запроса из LINQ в SQL выполняется один раз и используется повторно, что особенно полезно для часто выполняемых запросов.
2. Эффективное использование ресурсов
Поскольку запрос компилируется один раз и кэшируется*, это снижает загрузку ЦП и потребление ресурсов, связанных с процессом компиляции запроса.
*Замечание: EF Core автоматически компилирует и кэширует наиболее распространённые запросы, уменьшая необходимость делать это вручную.
Когда не использовать
1. Высокодинамичные запросы
Если запросы часто меняются или динамически конструируются на основе различных условий.
2. Редко выполняемые запросы
Выигрыш в производительности может быть незначительным.
3. Среды с ограниченными ресурсами
Если использование памяти ограничено, будьте осторожны с количеством скомпилированных запросов.
Итого
Скомпилированные запросы в EF Core — мощная функция для оптимизации доступа к БД. Они наиболее полезны для повышения производительности в сценариях с часто повторяющимися запросами. Однако их использование должно быть сбалансировано с учётом потенциальных недостатков, таких как повышенное использование памяти и сложность, особенно в приложениях с динамическими шаблонами запросов или ограниченными ресурсами.
Источник: https://stefandjokic.tech/blog
Повышаем Производительность EF Core с Помощью Скомпилированных Запросов
Скомпилированные запросы в .NET появились как ответ на проблемы с производительностью, возникающие при операциях извлечения данных, особенно в приложениях, использующих ORM, такие как Entity Framework.
До их появления каждая операция извлечения данных требовала от ORM преобразования LINQ в запросы SQL, а этот процесс занимал много времени и ресурсов.
Допустим, у нас есть класс модели данных:
public class User
{
public int Id { get; init; }
public string Name { get; init; }
public int Age { get; init; }
}
Стандартный код для извлечения пользователя по Id будет примерно таким:
using var dbCtx = new AppDbContext();
var user =
dbCtx.Users.FirstOrDefault(x => x.Id == x);
Создадим скомпилированный запрос:
public class AppDbContext
{
private static
Func<AppDbContext, int, User> getUser =
EF.CompileQuery(
(ctx, id) => ctx.User
.FirstOrDefault(n => n.Id == id));
public User GetUser(int id)
{
return getUser(this, id);
}
}
Здесь:
- Статическое поле getUser типа Func<AppDbContext, int, User> - это скомпилированный запрос с использованием метода EF.CompileQuery.
- Метод GetUser – принимает id в качестве параметра и использует статическое поле getUser для получения объекта User из БД.
Мы также можем использовать асинхронный скомпилированный запрос с помощью EF.CompileAsyncQuery. Тогда поле должно быть типа Func<AppDbContext, int, Task<User>>, а метод GetUser будет асинхронным:
public async Task<User> GetUserAsync(int id)
{
return await getUser(this, id);
}
Теперь можно вызвать метод, созданный в нашем контексте:
using var dbCtx = new AppDbContext();
var user = dbCtx.GetUser(id);
Зачем использовать скомпилированные запросы?
1. Производительность
Преобразование запроса из LINQ в SQL выполняется один раз и используется повторно, что особенно полезно для часто выполняемых запросов.
2. Эффективное использование ресурсов
Поскольку запрос компилируется один раз и кэшируется*, это снижает загрузку ЦП и потребление ресурсов, связанных с процессом компиляции запроса.
*Замечание: EF Core автоматически компилирует и кэширует наиболее распространённые запросы, уменьшая необходимость делать это вручную.
Когда не использовать
1. Высокодинамичные запросы
Если запросы часто меняются или динамически конструируются на основе различных условий.
2. Редко выполняемые запросы
Выигрыш в производительности может быть незначительным.
3. Среды с ограниченными ресурсами
Если использование памяти ограничено, будьте осторожны с количеством скомпилированных запросов.
Итого
Скомпилированные запросы в EF Core — мощная функция для оптимизации доступа к БД. Они наиболее полезны для повышения производительности в сценариях с часто повторяющимися запросами. Однако их использование должно быть сбалансировано с учётом потенциальных недостатков, таких как повышенное использование памяти и сложность, особенно в приложениях с динамическими шаблонами запросов или ограниченными ресурсами.
Источник: https://stefandjokic.tech/blog
👍23
День 1763. #ЗаметкиНаПолях
Оптимизируем Запросы EF Core.
Продолжаем разговор о быстродействии запросов в EF Core. Существует множество правил оптимизации запросов, сегодня рассмотрим основные.
1. Выбирайте только нужные поля
Рассмотрим пример:
В БД скорее всего будет больше 2 полей. Но нам не нужны остальные.
Преимущества
- Сокращает объём данных, передаваемых из БД в приложение, что поможет, особенно в распределенных средах.
- Снижает использование памяти приложения.
- БД может возвращать результат быстрее, например, за счёт покрывающих индексов.
- Уменьшаются накладные расходы на отслеживание изменений.
2. Избегайте N+1 запросов
Это происходит, когда ваше приложение выполняет один запрос для получения набора объектов, а затем дополнительные запросы для каждого объекта для получения связанных данных:
Вместо этого используем «жадную загрузку»:
«Жадная» загрузка с помощью Include(b => b.Posts) сообщает EF Core загрузить посты, связанные с каждым блогом, как часть изначального запроса. Таким образом выполняется один запрос, который извлекает все блоги со связанными с ними постами. Поэтому во время итерации в цикле foreach не выполняются никакие дополнительные запросы.
Преимущества
- Значительное снижение нагрузки на БД и сеть (особенно при грамотной организации данных: индексы, внешние ключи).
- Упрощение логики кода, поскольку в нём явно указано намерение загрузить связанные данные.
3. Используйте AsNoTracking()
Этот метод особенно полезен, когда вы только считываете данные из базы. В следующем примере список продуктов извлекается без затрат на отслеживание изменений:
Преимущества
- Особенно заметно в крупных приложениях или при работе с большими наборами данных. Запросы выполняются быстрее и потребляется меньше памяти.
- Сокращение накладных расходов, т.к. не требуется отслеживать изменения или сохранять информацию о состоянии объектов.
4. Используйте разделённые запросы с помощью AsSplitQuery().
Подробности здесь.
Источник: https://stefandjokic.tech/blog
Оптимизируем Запросы EF Core.
Продолжаем разговор о быстродействии запросов в EF Core. Существует множество правил оптимизации запросов, сегодня рассмотрим основные.
1. Выбирайте только нужные поля
Рассмотрим пример:
var employees = context.Employees
.Select(e => new EmployeeDto
{
Name = e.Name,
Email = e.Email
}).ToList();
В БД скорее всего будет больше 2 полей. Но нам не нужны остальные.
Преимущества
- Сокращает объём данных, передаваемых из БД в приложение, что поможет, особенно в распределенных средах.
- Снижает использование памяти приложения.
- БД может возвращать результат быстрее, например, за счёт покрывающих индексов.
- Уменьшаются накладные расходы на отслеживание изменений.
2. Избегайте N+1 запросов
Это происходит, когда ваше приложение выполняет один запрос для получения набора объектов, а затем дополнительные запросы для каждого объекта для получения связанных данных:
// Получаем все блоги - 1 запрос
var blogs = context.Blogs.ToList();
foreach (var b in blogs)
{
// Для каждого блога получаем посты
// N запросов
var posts = b.Posts;
foreach (var p in posts)
Console.WriteLine(p.Title);
}
Вместо этого используем «жадную загрузку»:
var blogs = context.Blogs
.Include(b => b.Posts).ToList();
foreach (var b in blogs)
{
foreach (var p in b.Posts)
Console.WriteLine(post.Title);
}
«Жадная» загрузка с помощью Include(b => b.Posts) сообщает EF Core загрузить посты, связанные с каждым блогом, как часть изначального запроса. Таким образом выполняется один запрос, который извлекает все блоги со связанными с ними постами. Поэтому во время итерации в цикле foreach не выполняются никакие дополнительные запросы.
Преимущества
- Значительное снижение нагрузки на БД и сеть (особенно при грамотной организации данных: индексы, внешние ключи).
- Упрощение логики кода, поскольку в нём явно указано намерение загрузить связанные данные.
3. Используйте AsNoTracking()
Этот метод особенно полезен, когда вы только считываете данные из базы. В следующем примере список продуктов извлекается без затрат на отслеживание изменений:
var products = context.Products
.AsNoTracking().ToList();
// Используем products только для чтения
Преимущества
- Особенно заметно в крупных приложениях или при работе с большими наборами данных. Запросы выполняются быстрее и потребляется меньше памяти.
- Сокращение накладных расходов, т.к. не требуется отслеживать изменения или сохранять информацию о состоянии объектов.
4. Используйте разделённые запросы с помощью AsSplitQuery().
Подробности здесь.
Источник: https://stefandjokic.tech/blog
👍14
День 1764. #ЗаметкиНаПолях #Debugging
Правила отладки: Меняйте За Раз Что-то Одно
Изменение нескольких элементов создаёт ряд проблем. Возникает вероятность мелких ошибок, очень похожих на первоначальную проблему. Т.е. вы не уверены, была ли исправлена проблема или не привело ли исправление случайно к аналогичной проблеме. Чтобы справиться с этой сложностью, вносите только одно изменение за раз.
Неизбирательные изменения могут нарушить работоспособность компонентов. Более того, точное определение неисправности позволяет решить только эту конкретную проблему. По сути, если вы считаете, что необходимы масштабные изменения, основная проблема, вероятно, заключается в отсутствии ясности относительно причины.
Во многих случаях желание модифицировать различные компоненты системы возникает с целью оценить их влияние на рассматриваемую проблему. Однако это часто служит тревожным сигналом, что вы гадаете, а не чётко понимаете проблему. Вместо тщательного наблюдения за возникновением сбоя вы хаотично меняете условия, что может скрыть первоначальную проблему и потенциально привести к возникновению других проблем.
Изолируйте ключевой фактор
Суть эффективной отладки в выявлении критического фактора путем сужения фокуса до определённого раздела кода, в котором может возникнуть потенциальная проблема. Так процесс отладки становится более точным и эффективным. Чтобы сделать это систематически исключайте части программы и наблюдайте, сохраняется ли ошибка. Нет ли при этом знакомой закономерности? Признание того, что «я это видел раньше», часто означает начало понимания, если не полное решение.
Меняйте один тест за раз
Иногда изменение нескольких тестов или корректировка нескольких параметров может привести к тому, что проблема будет проявляться более последовательно, обеспечивая более чёткое представление о сбое. Однако крайне важно придерживаться принципа изменения только одного элемента за раз. Это обеспечивает точную идентификацию параметра, ответственного за наблюдаемый эффект. Если изменение не влияет на ошибку, верните всё в исходное состояние.
Сравните плохое с хорошим
Изучая два сценария сбоя и успеха, сравните различные аспекты: трассировки, выходные данные отладки и окна состояния и т.п. Если между двумя тестами произошло множество изменений кода или созданы разные сценарии работы, их сравнение может стать затруднительным. Чтобы изолировать ошибку, важно свести к минимуму различия между ними. Если в неудачном тесте есть что-то, чего нет в удачном, вы на пути к обнаружению проблемы.
Изучите последнее изменение
Если придерживаться практики изменения только одного аспекта за раз при разработке, ошибка, скорее всего, либо окажется в новом коде, либо будет обнаружена им. Если ошибка проявляется в новой версии, но не в старой, это означает, что новый код является частью проблемы.
Однако иногда давняя проблема проявляется, только когда происходят другие изменения. Введение нового кода может создать условия, вызывающие сбой ранее надёжной подсистемы.
Если невозможно выявить дефект, запустите более старую версию программы, чтобы определить, сохраняется ли ошибка. Если нет, это значит, что ошибка связана с новой версией или связана с изменениями, внесёнными в новую версию. Изучите различия между старой и новой версиями. Изучите журнал системы контроля версий, чтобы определить последние изменения кода.
Очень важно стремиться к предсказуемости в процессе. Устраните изменения, которые не дали ожидаемых результатов, поскольку они, вероятно, влекут за собой неожиданные последствия.
Источник: https://dev.to/rajasegar/debugging-rules-change-one-thing-at-a-time-3kc6
Правила отладки: Меняйте За Раз Что-то Одно
Изменение нескольких элементов создаёт ряд проблем. Возникает вероятность мелких ошибок, очень похожих на первоначальную проблему. Т.е. вы не уверены, была ли исправлена проблема или не привело ли исправление случайно к аналогичной проблеме. Чтобы справиться с этой сложностью, вносите только одно изменение за раз.
Неизбирательные изменения могут нарушить работоспособность компонентов. Более того, точное определение неисправности позволяет решить только эту конкретную проблему. По сути, если вы считаете, что необходимы масштабные изменения, основная проблема, вероятно, заключается в отсутствии ясности относительно причины.
Во многих случаях желание модифицировать различные компоненты системы возникает с целью оценить их влияние на рассматриваемую проблему. Однако это часто служит тревожным сигналом, что вы гадаете, а не чётко понимаете проблему. Вместо тщательного наблюдения за возникновением сбоя вы хаотично меняете условия, что может скрыть первоначальную проблему и потенциально привести к возникновению других проблем.
Изолируйте ключевой фактор
Суть эффективной отладки в выявлении критического фактора путем сужения фокуса до определённого раздела кода, в котором может возникнуть потенциальная проблема. Так процесс отладки становится более точным и эффективным. Чтобы сделать это систематически исключайте части программы и наблюдайте, сохраняется ли ошибка. Нет ли при этом знакомой закономерности? Признание того, что «я это видел раньше», часто означает начало понимания, если не полное решение.
Меняйте один тест за раз
Иногда изменение нескольких тестов или корректировка нескольких параметров может привести к тому, что проблема будет проявляться более последовательно, обеспечивая более чёткое представление о сбое. Однако крайне важно придерживаться принципа изменения только одного элемента за раз. Это обеспечивает точную идентификацию параметра, ответственного за наблюдаемый эффект. Если изменение не влияет на ошибку, верните всё в исходное состояние.
Сравните плохое с хорошим
Изучая два сценария сбоя и успеха, сравните различные аспекты: трассировки, выходные данные отладки и окна состояния и т.п. Если между двумя тестами произошло множество изменений кода или созданы разные сценарии работы, их сравнение может стать затруднительным. Чтобы изолировать ошибку, важно свести к минимуму различия между ними. Если в неудачном тесте есть что-то, чего нет в удачном, вы на пути к обнаружению проблемы.
Изучите последнее изменение
Если придерживаться практики изменения только одного аспекта за раз при разработке, ошибка, скорее всего, либо окажется в новом коде, либо будет обнаружена им. Если ошибка проявляется в новой версии, но не в старой, это означает, что новый код является частью проблемы.
Однако иногда давняя проблема проявляется, только когда происходят другие изменения. Введение нового кода может создать условия, вызывающие сбой ранее надёжной подсистемы.
Если невозможно выявить дефект, запустите более старую версию программы, чтобы определить, сохраняется ли ошибка. Если нет, это значит, что ошибка связана с новой версией или связана с изменениями, внесёнными в новую версию. Изучите различия между старой и новой версиями. Изучите журнал системы контроля версий, чтобы определить последние изменения кода.
Очень важно стремиться к предсказуемости в процессе. Устраните изменения, которые не дали ожидаемых результатов, поскольку они, вероятно, влекут за собой неожиданные последствия.
Источник: https://dev.to/rajasegar/debugging-rules-change-one-thing-at-a-time-3kc6
👍6
День 1765. #ВопросыНаСобеседовании #Многопоточность
Самые часто задаваемые вопросы на собеседовании по C#
27. Опишите концепцию конфликта блокировок в многопоточности и объясните его влияние на производительность приложения. Как можно решить и смягчить проблемы, связанные с конфликтами блокировок?
Конфликт блокировок — это состояние, когда один поток ожидает другого, пытаясь получить блокировку. Какое бы время ни было потрачено на ожидание блокировки, это «потерянное» время, потраченное впустую. Очевидно, что это может вызвать серьезные проблемы с производительностью. Конфликты блокировок могут быть вызваны любым типом механизма синхронизации потоков. Это может быть из-за оператора блокировки, AutoResetEvent/ManualResetEvent, ReaderWriterLockSlim, Mutex или Semaphore и всех остальных. Всё, что заставляет один поток приостанавливать выполнение до тех пор, пока не поступит сигнал другого потока, может вызвать конфликт блокировок.
Влияние конфликта блокировок:
- Увеличение времени ожидания
Потоки, ожидающие снятия блокировки, испытывают увеличенную задержку, что снижает общую пропускную способность приложения.
- Снижение параллелизма
Когда несколько потоков ожидают блокировки, вероятность параллелизма снижается, что делает приложение менее эффективным в использовании аппаратных и системных ресурсов.
- Риск взаимоблокировок
Высокая конкуренция за блокировки может увеличить риск взаимоблокировок, когда два или более потоков ждут блокировок, удерживаемых друг другом.
Стратегии для устранения или смягчения проблемы:
1. Детализируйте блокировку
Вместо блокировки всей структуры данных или ресурса заблокируйте более мелкие части, чтобы позволить большему количеству потоков одновременно получать доступ к различным разделам.
2. Сократите продолжительность блокировки
Минимизируйте время, проводимое внутри заблокированной области, выполняя только важные операции и перемещая некритические задачи за пределы заблокированной области.
3. Используйте структуры данных и алгоритмы без блокировки
Если возможно, используйте неблокирующие алгоритмы и структуры данных, которые не полагаются на блокировки, такие как ConcurrentQueue, ConcurrentDictionary или ConcurrentBag.
4. Используйте блокировки чтения-записи
Используйте ReaderWriterLockSlim, когда операций чтения больше, чем операций записи, что позволяет использовать несколько операций чтения, сохраняя при этом монопольный доступ к записи.
5. Минимизируйте конфликты с помощью секционирования
Разделите данные на секции, обрабатываемые отдельными потоками, что снизит необходимость синхронизации.
6. Избегайте вложенных блокировок
Уменьшите риск взаимоблокировок и конфликтов, избегая вложенных блокировок или иерархий блокировок.
Применяя эти стратегии, вы можете устранить и смягчить проблемы конфликтов блокировок в многопоточном приложении, улучшая как производительность, так и надёжность приложения.
Источник: https://dev.to/bytehide/c-multithreading-interview-questions-and-answers-4opj
Самые часто задаваемые вопросы на собеседовании по C#
27. Опишите концепцию конфликта блокировок в многопоточности и объясните его влияние на производительность приложения. Как можно решить и смягчить проблемы, связанные с конфликтами блокировок?
Конфликт блокировок — это состояние, когда один поток ожидает другого, пытаясь получить блокировку. Какое бы время ни было потрачено на ожидание блокировки, это «потерянное» время, потраченное впустую. Очевидно, что это может вызвать серьезные проблемы с производительностью. Конфликты блокировок могут быть вызваны любым типом механизма синхронизации потоков. Это может быть из-за оператора блокировки, AutoResetEvent/ManualResetEvent, ReaderWriterLockSlim, Mutex или Semaphore и всех остальных. Всё, что заставляет один поток приостанавливать выполнение до тех пор, пока не поступит сигнал другого потока, может вызвать конфликт блокировок.
Влияние конфликта блокировок:
- Увеличение времени ожидания
Потоки, ожидающие снятия блокировки, испытывают увеличенную задержку, что снижает общую пропускную способность приложения.
- Снижение параллелизма
Когда несколько потоков ожидают блокировки, вероятность параллелизма снижается, что делает приложение менее эффективным в использовании аппаратных и системных ресурсов.
- Риск взаимоблокировок
Высокая конкуренция за блокировки может увеличить риск взаимоблокировок, когда два или более потоков ждут блокировок, удерживаемых друг другом.
Стратегии для устранения или смягчения проблемы:
1. Детализируйте блокировку
Вместо блокировки всей структуры данных или ресурса заблокируйте более мелкие части, чтобы позволить большему количеству потоков одновременно получать доступ к различным разделам.
2. Сократите продолжительность блокировки
Минимизируйте время, проводимое внутри заблокированной области, выполняя только важные операции и перемещая некритические задачи за пределы заблокированной области.
3. Используйте структуры данных и алгоритмы без блокировки
Если возможно, используйте неблокирующие алгоритмы и структуры данных, которые не полагаются на блокировки, такие как ConcurrentQueue, ConcurrentDictionary или ConcurrentBag.
4. Используйте блокировки чтения-записи
Используйте ReaderWriterLockSlim, когда операций чтения больше, чем операций записи, что позволяет использовать несколько операций чтения, сохраняя при этом монопольный доступ к записи.
5. Минимизируйте конфликты с помощью секционирования
Разделите данные на секции, обрабатываемые отдельными потоками, что снизит необходимость синхронизации.
6. Избегайте вложенных блокировок
Уменьшите риск взаимоблокировок и конфликтов, избегая вложенных блокировок или иерархий блокировок.
Применяя эти стратегии, вы можете устранить и смягчить проблемы конфликтов блокировок в многопоточном приложении, улучшая как производительность, так и надёжность приложения.
Источник: https://dev.to/bytehide/c-multithreading-interview-questions-and-answers-4opj
👍24
This media is not supported in your browser
VIEW IN TELEGRAM
День 1766. #ЧтоНовенького
Пишем Сообщения Коммитов с Помощью GitHub Copilot
В последней превью версии Visual Studio добавлена новая функция –автоматическое описание изменений в сообщении коммита.
Чтобы опробовать новую функцию, загрузите последнюю превью версию Visual Studio и обновите расширение GitHub Copilot Chat Extension. Вам также понадобится активная подписка GitHub Copilot.
Новая функция Generated Commit Message (Сгенерированное Сообщение Коммита) использует GitHub Copilot для описания изменений вашего кода. Благодаря этому написание описательных и полезных сообщений о коммитах становится таким же простым, как нажатие кнопки и последующее добавление пояснений.
Используйте новый значок ручки в окне Git Changes, чтобы сгенерировать предложение. GitHub Copilot проверит изменения файлов в вашем коммите, обобщит их, а затем опишет каждое изменение. См. видео. Затем вы можете использовать предложение или отменить его, а также добавить свои комментарии.
Источник
Пишем Сообщения Коммитов с Помощью GitHub Copilot
В последней превью версии Visual Studio добавлена новая функция –автоматическое описание изменений в сообщении коммита.
Чтобы опробовать новую функцию, загрузите последнюю превью версию Visual Studio и обновите расширение GitHub Copilot Chat Extension. Вам также понадобится активная подписка GitHub Copilot.
Новая функция Generated Commit Message (Сгенерированное Сообщение Коммита) использует GitHub Copilot для описания изменений вашего кода. Благодаря этому написание описательных и полезных сообщений о коммитах становится таким же простым, как нажатие кнопки и последующее добавление пояснений.
Используйте новый значок ручки в окне Git Changes, чтобы сгенерировать предложение. GitHub Copilot проверит изменения файлов в вашем коммите, обобщит их, а затем опишет каждое изменение. См. видео. Затем вы можете использовать предложение или отменить его, а также добавить свои комментарии.
Источник
👍9
День 1767. #Оффтоп
А вы знали, что даже в таком, казалось бы, простом алгоритме, как бинарный поиск, может быть баг? Причём, не где-нибудь, а в реализации в Java. И просуществовал он там почти 10 лет!
Об этом в новом видео на канале Computerphile.
А если интересно узнать, либо вспомнить, собственно алгоритм, то объяснение здесь.
А вы знали, что даже в таком, казалось бы, простом алгоритме, как бинарный поиск, может быть баг? Причём, не где-нибудь, а в реализации в Java. И просуществовал он там почти 10 лет!
Об этом в новом видео на канале Computerphile.
А если интересно узнать, либо вспомнить, собственно алгоритм, то объяснение здесь.
YouTube
Bug in Binary Search - Computerphile
Mike talks through a binary search bug that was undiscovered for years!
https://www.facebook.com/computerphile
https://twitter.com/computer_phile
This video was filmed and edited by Sean Riley.
Computer Science at the University of Nottingham: https:/…
https://www.facebook.com/computerphile
https://twitter.com/computer_phile
This video was filmed and edited by Sean Riley.
Computer Science at the University of Nottingham: https:/…
👍6