День 1849. #ProjectManagement
Отслеживаем Архитектурные Решения через ADR
При проектировании архитектуры системы у вас есть много вариантов выбора. Вы архитектор, делаете некоторый выбор, всё идёт хорошо несколько месяцев. И вдруг появляется новое требование. Оно заставляет задуматься: «Правильна ли эта архитектура? Стоит ли что-то менять?». Вы уже не помните причины своего выбора. Нужно что-то, что напоминало бы вам, почему вы сделали тот или иной выбор…
Реестр Архитектурных Решений (Architecture Decision Records, ADR) — это способ описания, отслеживания и обсуждения архитектурных и проектных решений. Он позволяет иметь чётко определённый процесс принятия обоснованных решений и отслеживать причины вашего выбора.
Структура
ADR — это не просто список решений. Это документ, в котором также отслеживается текущий контекст, рассматриваемые альтернативы и последствия окончательного выбора. ADR состоит из набора файлов, каждый из которых описывает решение.
1. Суть решения
Например, «Использовать Azure Service Bus для очередей».
2. Статус
Например, «На обсуждении», «Принято» и «Заменено».
3. Время принятия решения
Когда ADR создан и когда получал каждый статус.
4. Контекст
Например: «В настоящее время мы используем Azure в качестве основного поставщика».
5. Последствия
Например: «Мы согласны с привязкой к поставщику. Так будет легче получить поддержку со стороны команды по инфраструктуре».
Статусы
Обычно должен быть строгий набор статусов, гарантирующий, что после достижения одного из окончательных статусов решение в ADR не может быть изменено.
Первичные
- Черновик: первичное описание решения, вы пока не готовы его обсуждать.
- Предложение: объяснены все детали решения для официального обсуждения.
- Открыто: каждый может добавлять комментарии к решению, что позволит учесть другие точки зрения и указать слабые места.
Окончательные
- Принято: команда согласна с решением, и теперь можно его реализовать.
- Отклонено: решение неосуществимо, реализация невозможна.
- Устарело: решение больше не является полезным или необходимым.
- Заменено: другой ADR заменяет нынешний. Обязательно нужно указать номер нового ADR, а в новом сослаться на тот, который он заменяет. Так можно восстановить историю конкретного выбора.
Рекомендации
1. Используйте единый формат и структуру для каждого ADR.
2. Храните ADR в текстовом файле рядом с кодом, соответствующим этому решению, или в центральном репозитории, чтобы можно было просмотреть историю обновлений.
3. Используйте понятное и описательное имя файла, например ADR-001-use-azure-service-bus.md.
4. Делайте ADR кратким и сосредоточенными на одном решении. Добавляйте только необходимую информацию, чтобы понять причину решения.
5. Обновляйте ADR по мере развития или изменения решения, указывая причину изменений.
6. Периодически проверяйте актуальность ADR текущему состоянию архитектуры и потребностям бизнеса.
Вот хороший список шаблонов ADR.
Инструменты
- adr-cli - инструмент CLI, написанный на .NET, который автоматически создаёт и управляет историей ваших ADR.
- ADR Manager - UI инструмент, который подключается к вашему репозиторию GitHub и генерирует файлы ADR.
Итого
ADR — отличный инструмент, особенно для проектов, требования которых не определены заранее и которые, как ожидается, будут иметь длительный срок службы. Одним из наиболее важных этапов принятия архитектурных решений является обсуждение решения. Архитектор не должен навязывать архитектуру команде. Другие заинтересованные стороны, например разработчики, могут иметь некоторые опасения или заметить случай, который не может быть охвачен решением.
Источник: https://www.code4it.dev/architecture-notes/architecture-decision-records/
Отслеживаем Архитектурные Решения через ADR
При проектировании архитектуры системы у вас есть много вариантов выбора. Вы архитектор, делаете некоторый выбор, всё идёт хорошо несколько месяцев. И вдруг появляется новое требование. Оно заставляет задуматься: «Правильна ли эта архитектура? Стоит ли что-то менять?». Вы уже не помните причины своего выбора. Нужно что-то, что напоминало бы вам, почему вы сделали тот или иной выбор…
Реестр Архитектурных Решений (Architecture Decision Records, ADR) — это способ описания, отслеживания и обсуждения архитектурных и проектных решений. Он позволяет иметь чётко определённый процесс принятия обоснованных решений и отслеживать причины вашего выбора.
Структура
ADR — это не просто список решений. Это документ, в котором также отслеживается текущий контекст, рассматриваемые альтернативы и последствия окончательного выбора. ADR состоит из набора файлов, каждый из которых описывает решение.
1. Суть решения
Например, «Использовать Azure Service Bus для очередей».
2. Статус
Например, «На обсуждении», «Принято» и «Заменено».
3. Время принятия решения
Когда ADR создан и когда получал каждый статус.
4. Контекст
Например: «В настоящее время мы используем Azure в качестве основного поставщика».
5. Последствия
Например: «Мы согласны с привязкой к поставщику. Так будет легче получить поддержку со стороны команды по инфраструктуре».
Статусы
Обычно должен быть строгий набор статусов, гарантирующий, что после достижения одного из окончательных статусов решение в ADR не может быть изменено.
Первичные
- Черновик: первичное описание решения, вы пока не готовы его обсуждать.
- Предложение: объяснены все детали решения для официального обсуждения.
- Открыто: каждый может добавлять комментарии к решению, что позволит учесть другие точки зрения и указать слабые места.
Окончательные
- Принято: команда согласна с решением, и теперь можно его реализовать.
- Отклонено: решение неосуществимо, реализация невозможна.
- Устарело: решение больше не является полезным или необходимым.
- Заменено: другой ADR заменяет нынешний. Обязательно нужно указать номер нового ADR, а в новом сослаться на тот, который он заменяет. Так можно восстановить историю конкретного выбора.
Рекомендации
1. Используйте единый формат и структуру для каждого ADR.
2. Храните ADR в текстовом файле рядом с кодом, соответствующим этому решению, или в центральном репозитории, чтобы можно было просмотреть историю обновлений.
3. Используйте понятное и описательное имя файла, например ADR-001-use-azure-service-bus.md.
4. Делайте ADR кратким и сосредоточенными на одном решении. Добавляйте только необходимую информацию, чтобы понять причину решения.
5. Обновляйте ADR по мере развития или изменения решения, указывая причину изменений.
6. Периодически проверяйте актуальность ADR текущему состоянию архитектуры и потребностям бизнеса.
Вот хороший список шаблонов ADR.
Инструменты
- adr-cli - инструмент CLI, написанный на .NET, который автоматически создаёт и управляет историей ваших ADR.
- ADR Manager - UI инструмент, который подключается к вашему репозиторию GitHub и генерирует файлы ADR.
Итого
ADR — отличный инструмент, особенно для проектов, требования которых не определены заранее и которые, как ожидается, будут иметь длительный срок службы. Одним из наиболее важных этапов принятия архитектурных решений является обсуждение решения. Архитектор не должен навязывать архитектуру команде. Другие заинтересованные стороны, например разработчики, могут иметь некоторые опасения или заметить случай, который не может быть охвачен решением.
Источник: https://www.code4it.dev/architecture-notes/architecture-decision-records/
👍13👎1
День 1850.
Сделал лазерную коррекцию зрения.
Если кому интересно, делал в Питере в клинике «Счастливый взгляд». Подробно о них и о коррекции вообще можно посмотреть в этом видео (кстати, мне делал тот же доктор).
Делал по методу SMILE. Он самый дорогой, но наименее инвазивный. На второй день уже можно садиться за руль или работать. В первый день это кажется нереальным, потому что глаза сильно щиплет, когда отходит наркоз. Но уже на следующее утро в принципе терпимо и всё видно. Хотя я всё-таки взял недельный отпуск, на всякий случай.
Существуют ещё 2 метода, более (сильно более) дешёвых. LASIK и ФРК. LASIK я хотел делать изначально, но по результатам обследования у меня выявили тонкую роговицу (наследственное), а при этом такой вид коррекции делать не рекомендуется. Предложили ФРК, но там довольно длительный и муторный процесс восстановления, поэтому поехал в Питер на SMILE.
Пока идёт третий день после операции, так полностью эффекта ещё не ощутил. Да и у меня было всего -1,75. Не то, чтоб совсем не видел. На следующий день после операции зрение проверили, сказали, что сейчас 0,9-1. Мне объясняли, но я так до конца и не понял, чем отличаются диоптрии (+/- 1,2,3,4…) от остроты зрения (0,1-1). Но суть в том, что у меня как раз был небольшой минус, а вот острота зрения была всего 0,2 (20%).
В принципе, и доктор после обследования тоже сказал, мол, решайте сами, можно и не делать, только очки подобрать. Потому что после 40 зрение начнёт корректироваться в сторону дальнозоркости, так что возможно потом глаза будут уставать от чтения и работы за компьютером, и надо будет очки для чтения/компьютера покупать (хотя, их и так надо будет). Но у меня ещё были некоторые проблемы, которые коррекция должна была решить, поэтому я решил делать.
Операция абсолютно безболезненная (только страшно, потому что что-то делают с глазами) и иногда неприятно, например, когда ставят расширитель на веки или когда вентикулу вынимают. И главное не дёргаться, что у меня получалось с трудом. Чуть не завалил весь процесс. Заняло всё 15 минут от входа в операционный отсек до выхода: переодевания, подготовка, операция. После идёшь домой и желательно поспать. В первый день ощущения не из приятных. Как будто постоянно смотришь на солнце (даже с закрытыми глазами). Но в принципе терпимо, через 4-6 часов это проходит. Остаётся небольшое помутнение зрения из-за воспаления, оно должно постепенно проходить и пройти полностью через пару недель.
Вот такие приключения.
Если кто тоже думает делать и имеет вопросы, спрашивайте. А кто делал, делитесь опытом в комментариях.
Сделал лазерную коррекцию зрения.
Если кому интересно, делал в Питере в клинике «Счастливый взгляд». Подробно о них и о коррекции вообще можно посмотреть в этом видео (кстати, мне делал тот же доктор).
Делал по методу SMILE. Он самый дорогой, но наименее инвазивный. На второй день уже можно садиться за руль или работать. В первый день это кажется нереальным, потому что глаза сильно щиплет, когда отходит наркоз. Но уже на следующее утро в принципе терпимо и всё видно. Хотя я всё-таки взял недельный отпуск, на всякий случай.
Существуют ещё 2 метода, более (сильно более) дешёвых. LASIK и ФРК. LASIK я хотел делать изначально, но по результатам обследования у меня выявили тонкую роговицу (наследственное), а при этом такой вид коррекции делать не рекомендуется. Предложили ФРК, но там довольно длительный и муторный процесс восстановления, поэтому поехал в Питер на SMILE.
Пока идёт третий день после операции, так полностью эффекта ещё не ощутил. Да и у меня было всего -1,75. Не то, чтоб совсем не видел. На следующий день после операции зрение проверили, сказали, что сейчас 0,9-1. Мне объясняли, но я так до конца и не понял, чем отличаются диоптрии (+/- 1,2,3,4…) от остроты зрения (0,1-1). Но суть в том, что у меня как раз был небольшой минус, а вот острота зрения была всего 0,2 (20%).
В принципе, и доктор после обследования тоже сказал, мол, решайте сами, можно и не делать, только очки подобрать. Потому что после 40 зрение начнёт корректироваться в сторону дальнозоркости, так что возможно потом глаза будут уставать от чтения и работы за компьютером, и надо будет очки для чтения/компьютера покупать (хотя, их и так надо будет). Но у меня ещё были некоторые проблемы, которые коррекция должна была решить, поэтому я решил делать.
Операция абсолютно безболезненная (только страшно, потому что что-то делают с глазами) и иногда неприятно, например, когда ставят расширитель на веки или когда вентикулу вынимают. И главное не дёргаться, что у меня получалось с трудом. Чуть не завалил весь процесс. Заняло всё 15 минут от входа в операционный отсек до выхода: переодевания, подготовка, операция. После идёшь домой и желательно поспать. В первый день ощущения не из приятных. Как будто постоянно смотришь на солнце (даже с закрытыми глазами). Но в принципе терпимо, через 4-6 часов это проходит. Остаётся небольшое помутнение зрения из-за воспаления, оно должно постепенно проходить и пройти полностью через пару недель.
Вот такие приключения.
Если кто тоже думает делать и имеет вопросы, спрашивайте. А кто делал, делитесь опытом в комментариях.
👍56
День 1851. #Шпаргалка
Примеры Кода для Повседневных Задач
В этой серии представлю вам коллекцию фрагментов кода C#, охватывающих широкий спектр сценариев, с которыми вы можете столкнуться при разработке ПО.
III. Операции с файлами
Операции с файлами являются общим требованием во многих задачах. В этом разделе мы рассмотрим, как работать с файлами и каталогами в C#, используя пространство имён
1. Чтение текстового файла
Чтобы прочитать текстовый файл в C#, вы можете использовать класс File и его метод ReadAllText():
2. Запись текстового файла
Чтобы записать текстовый файл, можно использовать метод WriteAllText() класса File:
3. Дозапись текста в файл
Чтобы добавить текст в текущий файл, можно использовать метод AppendAllText():
4. Чтение файла построчно
Для чтения файла построчно подойдёт метод ReadLines():
5. Создание/удаление папки
Для создания/удаления папки используйте соответственно методы CreateDirectory() и Delete() класса Directory:
Второй параметр метода Delete указывает, надо ли удалять всё содержимое папки.
6. Проверка существования
Чтобы проверить существование файла или папки, используйте метод Exists() класса File или Directory соответственно:
7. Список файлов в папке
Используйте метод GetFiles() класса Directory:
8. Копия/перемещение файла
Используйте соответственно методы Copy() или Move() класса File:
Источник: https://medium.com/bytehide/100-csharp-code-snippets-for-everyday-problems-e913c786dec9
Примеры Кода для Повседневных Задач
В этой серии представлю вам коллекцию фрагментов кода C#, охватывающих широкий спектр сценариев, с которыми вы можете столкнуться при разработке ПО.
III. Операции с файлами
Операции с файлами являются общим требованием во многих задачах. В этом разделе мы рассмотрим, как работать с файлами и каталогами в C#, используя пространство имён
System.IO
.1. Чтение текстового файла
Чтобы прочитать текстовый файл в C#, вы можете использовать класс File и его метод ReadAllText():
var path = "example.txt";
var content = File.ReadAllText(path);
Console.WriteLine(content);
2. Запись текстового файла
Чтобы записать текстовый файл, можно использовать метод WriteAllText() класса File:
var path = "output.txt";
var content = "Hello, World!";
File.WriteAllText(path, content);
3. Дозапись текста в файл
Чтобы добавить текст в текущий файл, можно использовать метод AppendAllText():
var path = "log.txt";
var logEntry = "New log entry";
File.AppendAllText(path, logEntry);
4. Чтение файла построчно
Для чтения файла построчно подойдёт метод ReadLines():
var path = "example.txt";
foreach (var line in File.ReadLines(path))
Console.WriteLine(line);
5. Создание/удаление папки
Для создания/удаления папки используйте соответственно методы CreateDirectory() и Delete() класса Directory:
var path = "new_directory";
Directory.CreateDirectory(path);
Directory.Delete(path, true);
Второй параметр метода Delete указывает, надо ли удалять всё содержимое папки.
6. Проверка существования
Чтобы проверить существование файла или папки, используйте метод Exists() класса File или Directory соответственно:
var fPath = "example.txt";
var dPath = "directory";
var fileExists = File.Exists(fPath);
var dirExists = Directory.Exists(dPath);
7. Список файлов в папке
Используйте метод GetFiles() класса Directory:
var path = " directory";
var files = Directory.GetFiles(path);
foreach (var f in files)
Console.WriteLine(f);
8. Копия/перемещение файла
Используйте соответственно методы Copy() или Move() класса File:
var source = "example.txt";
var copy = "copy_example.txt";
var moved = "moved_example.txt";
File.Copy(source, copy);
File.Move(source, moved);
Источник: https://medium.com/bytehide/100-csharp-code-snippets-for-everyday-problems-e913c786dec9
👍17👎1
Что произойдёт при выполнении следующего кода?
List<int> nums = [1, 42, 69]; nums.Remove(1); nums.Remove(2);
#Quiz #CSharp
List<int> nums = [1, 42, 69]; nums.Remove(1); nums.Remove(2);
#Quiz #CSharp
Anonymous Quiz
16%
Будет удалён только 1й элемент
3%
Будет удалён только 2й элемент
16%
Будут удалены 2й и 3й элементы
65%
Будет удалён 1й элемент, и возникнет ошибка, т.к. нет элемента 2
👍18👎1
День 1853. #ЗаметкиНаПолях
Автоматически Регистрируем Конечные Точки в Минимальных API
В минимальных API регистрация каждой конечной точки через app.MapGet, app.MapPost и т.п. может привести к появлению повторяющегося кода. По мере роста проекта этот ручной процесс становится всё более трудоёмким и загромождать файл Program.cs. Можно попробовать группировать конечные точки, используя методы расширения, но это похоже на переизобретение контроллеров.
Автоматическая регистрация минимальных API значительно упрощает разработку, делая кодовую базу более лаконичной и улучшает удобство сопровождения за счёт создания централизованного механизма регистрации.
Сначала создадим интерфейс. Метод MapEndpoint принимает IEndpointRouteBuilder, который мы сможем использовать для вызова MapGet, MapPost и т.п.
Каждая реализация IEndpoint должна содержать ровно одно определение конечной точки. Ничто не мешает регистрировать несколько, но лучше не стоит.
Теперь используем рефлексию для сканирования сборок .NET и поиска классов, реализующих IEndpoint:
Наконец создадим метод расширения для регистрации конечных точек. Мы ищем все регистрации IEndpoint. Это будут классы конечных точек, которые мы теперь можем зарегистрировать в приложении, вызвав их метод MapEndpoint. Здесь мы также добавим параметр RouteGroupBuilder для возможности группировки конечных точек.
Вот как будет выглядеть файл Program.cs:
- AddEndpoints регистрирует реализации IEndpoint,
- MapEndpoints регистрирует конечные точки.
Итого
Автоматическая регистрация конечных точек в минимальных API может значительно повысить эффективность разработки и удобство сопровождения проекта. Хотя важно признать потенциальное влияние рефлексии на производительность при запуске приложения, а также на возможность использовать Native AOT. Потенциальным улучшением может стать использование генераторов кода для логики регистрации.
Полный код проекта тут.
Источник: https://www.milanjovanovic.tech/blog/automatically-register-minimal-apis-in-aspnetcore
Автоматически Регистрируем Конечные Точки в Минимальных API
В минимальных API регистрация каждой конечной точки через app.MapGet, app.MapPost и т.п. может привести к появлению повторяющегося кода. По мере роста проекта этот ручной процесс становится всё более трудоёмким и загромождать файл Program.cs. Можно попробовать группировать конечные точки, используя методы расширения, но это похоже на переизобретение контроллеров.
Автоматическая регистрация минимальных API значительно упрощает разработку, делая кодовую базу более лаконичной и улучшает удобство сопровождения за счёт создания централизованного механизма регистрации.
Сначала создадим интерфейс. Метод MapEndpoint принимает IEndpointRouteBuilder, который мы сможем использовать для вызова MapGet, MapPost и т.п.
public interface IEndpoint
{
void MapEndpoint(IEndpointRouteBuilder app);
}
Каждая реализация IEndpoint должна содержать ровно одно определение конечной точки. Ничто не мешает регистрировать несколько, но лучше не стоит.
public class GetUserStats : IEndpoint
{
public void MapEndpoint(IEndpointRouteBuilder app)
{
app.MapGet(
"users/{id}/stats",
async (Guid id) =>
{
return await GetStats(id);
}
);
}
}
Теперь используем рефлексию для сканирования сборок .NET и поиска классов, реализующих IEndpoint:
public static IServiceCollection AddEndpoints(
this IServiceCollection services,
Assembly assembly)
{
var svcs = assembly.DefinedTypes
.Where(t => t is
{ IsAbstract: false, IsInterface: false } &&
t.IsAssignableTo(typeof(IEndpoint)))
.Select(t =>
ServiceDescriptor.Transient(typeof(IEndpoint), t))
.ToArray();
services.TryAddEnumerable(svcs);
return services;
}
Наконец создадим метод расширения для регистрации конечных точек. Мы ищем все регистрации IEndpoint. Это будут классы конечных точек, которые мы теперь можем зарегистрировать в приложении, вызвав их метод MapEndpoint. Здесь мы также добавим параметр RouteGroupBuilder для возможности группировки конечных точек.
public static IApplicationBuilder MapEndpoints(
this WebApplication app,
RouteGroupBuilder? rgb = null)
{
var endpoints = app.Services
.GetRequiredService<IEnumerable<IEndpoint>>();
// Регистрируем в группу, если она задана.
// Если нет, то на уровне приложения
var builder = rgb is null ? app : rgb;
foreach (IEndpoint ep in endpoints)
ep.MapEndpoint(builder);
return app;
}
Вот как будет выглядеть файл Program.cs:
- AddEndpoints регистрирует реализации IEndpoint,
- MapEndpoints регистрирует конечные точки.
var builder =
WebApplication.CreateBuilder(args);
…
builder.Services.AddEndpoints(
typeof(Program).Assembly);
var app = builder.Build();
…
app.MapEndpoints();
app.Run();
Итого
Автоматическая регистрация конечных точек в минимальных API может значительно повысить эффективность разработки и удобство сопровождения проекта. Хотя важно признать потенциальное влияние рефлексии на производительность при запуске приложения, а также на возможность использовать Native AOT. Потенциальным улучшением может стать использование генераторов кода для логики регистрации.
Полный код проекта тут.
Источник: https://www.milanjovanovic.tech/blog/automatically-register-minimal-apis-in-aspnetcore
👍14
День 1854. #ЧтоНовенького
Планы на .NET 9
Мы находимся в начале очередного ежегодного цикла выпусков .NET. Microsoft поделились своим видением на .NET 9, релиз которого запланирован на .NET Conf 2024 в конце года. Наиболее важными направлениями являются разработка облачных приложений, значительные инвестиции в производительность и безопасность, а также развитие всей платформы. Первая превью версия .NET 9 уже выпущена.
Платформа для облачной разработки
В .NET 9 будет продолжено развитие облачных технологий с упором
на производительность во время выполнения и мониторинг приложений. Также будет уделено внимание упрощению интеграции с популярными сервисами, например, Kubernetes или Redis. Все эти возможности объединены в .NET Aspire, что значительно снизит стоимость и сложность создания облачных приложений.
Продолжится разработка Native AOT и тримминг приложений как ключевых инструментов для оптимизации рабочих приложений. В .NET 8 оптимизированы приложения веб-API, в .NET 9 будут оптимизированы другие типы приложений, а также обещано дальнейшее улучшение сборщика данных для динамической адаптации к размерам приложений (DATAS).
В Azure Container Apps позаботятся о том, чтобы приложения .NET 9 можно было легко масштабировать до нескольких экземпляров в их Kubernetes-среде. Также планируются улучшения безопасности при работе с такими данными, как токены защиты от подделки и токены аутентификации, и оптимизация API ограничений при работе с несколькими экземплярами.
Пример приложения eShop, который был продемонстрирован на .NET Conf в прошлом году, будет обновлён, чтобы использовать преимущества этих новых возможностей и вариантов развертывания по мере развития .NET 9 в течение года.
Visual Studio и VS Code будут включать новые возможности разработки и развёртывания для .NET Aspire: настройку компонентов, отладку (включая горячую перезагрузку) хоста приложений и дочерних процессов, а также полную интеграцию с панелью разработчика. Разработчики смогут развёртывать свои проекты в приложениях-контейнерах Azure из Visual Studio, VS Code и с помощью Azure Developer CLI.
Дорожная карта .NET 9
Здесь можно посмотреть детальные дорожные карты по разработке новинок в каждой сфере, будь то C#, ASP.NET, Entity Framework или Roslyn. Пока многие из них пустые, но в скором времени будут обновлены.
PS: если вы ждали Discriminated Unions в C#, то их там (пока) нет. Вообще новинок в C# довольно мало, и они сильно узкоспециализированные. Надеюсь, что в течение года появится что-то более интересное.
Источник: https://devblogs.microsoft.com/dotnet/our-vision-for-dotnet-9/
Планы на .NET 9
Мы находимся в начале очередного ежегодного цикла выпусков .NET. Microsoft поделились своим видением на .NET 9, релиз которого запланирован на .NET Conf 2024 в конце года. Наиболее важными направлениями являются разработка облачных приложений, значительные инвестиции в производительность и безопасность, а также развитие всей платформы. Первая превью версия .NET 9 уже выпущена.
Платформа для облачной разработки
В .NET 9 будет продолжено развитие облачных технологий с упором
на производительность во время выполнения и мониторинг приложений. Также будет уделено внимание упрощению интеграции с популярными сервисами, например, Kubernetes или Redis. Все эти возможности объединены в .NET Aspire, что значительно снизит стоимость и сложность создания облачных приложений.
Продолжится разработка Native AOT и тримминг приложений как ключевых инструментов для оптимизации рабочих приложений. В .NET 8 оптимизированы приложения веб-API, в .NET 9 будут оптимизированы другие типы приложений, а также обещано дальнейшее улучшение сборщика данных для динамической адаптации к размерам приложений (DATAS).
В Azure Container Apps позаботятся о том, чтобы приложения .NET 9 можно было легко масштабировать до нескольких экземпляров в их Kubernetes-среде. Также планируются улучшения безопасности при работе с такими данными, как токены защиты от подделки и токены аутентификации, и оптимизация API ограничений при работе с несколькими экземплярами.
Пример приложения eShop, который был продемонстрирован на .NET Conf в прошлом году, будет обновлён, чтобы использовать преимущества этих новых возможностей и вариантов развертывания по мере развития .NET 9 в течение года.
Visual Studio и VS Code будут включать новые возможности разработки и развёртывания для .NET Aspire: настройку компонентов, отладку (включая горячую перезагрузку) хоста приложений и дочерних процессов, а также полную интеграцию с панелью разработчика. Разработчики смогут развёртывать свои проекты в приложениях-контейнерах Azure из Visual Studio, VS Code и с помощью Azure Developer CLI.
Дорожная карта .NET 9
Здесь можно посмотреть детальные дорожные карты по разработке новинок в каждой сфере, будь то C#, ASP.NET, Entity Framework или Roslyn. Пока многие из них пустые, но в скором времени будут обновлены.
PS: если вы ждали Discriminated Unions в C#, то их там (пока) нет. Вообще новинок в C# довольно мало, и они сильно узкоспециализированные. Надеюсь, что в течение года появится что-то более интересное.
Источник: https://devblogs.microsoft.com/dotnet/our-vision-for-dotnet-9/
👍13
День 1855. #Шпаргалка
Шпаргалка по LINQ
См. подробнее
- Книга «LINQ Explained with sketches»
- Новые методы LINQ в .NET 9
См. также
- LINQ на Стероидах с SIMD
- Parallel LINQ
- Использование LINQ с асинхронными потоками
Источник: https://steven-giesel.com/blogPost/62049533-001a-46fb-a1da-88effc0183f1/linq-mindmap-net-9-edition
Шпаргалка по LINQ
См. подробнее
- Книга «LINQ Explained with sketches»
- Новые методы LINQ в .NET 9
См. также
- LINQ на Стероидах с SIMD
- Parallel LINQ
- Использование LINQ с асинхронными потоками
Источник: https://steven-giesel.com/blogPost/62049533-001a-46fb-a1da-88effc0183f1/linq-mindmap-net-9-edition
👍28👎1
День 1856. #ЗаметкиНаПолях
Получаем Все Исключения из Parallel.ForEachAsync
На C# вы можете написать асинхронный код, похожий на синхронный код. Люди, использующие async/await, ожидают, что смогут перехватывать исключения, как в синхронном коде. Это очень удобно, но иногда вам нужно получить все исключения из асинхронного метода.
Если вы хотите получить все исключения, необходимо использовать Task.Exceptions или метод, предоставляющий содержимое этого метода, например Task.Wait.
Заметьте, что Parallel.ForEachAsync прекращает обработку элементов из коллекции после выброса первого исключения. Он ожидает завершения текущих задач, а затем сообщает об исключениях. Количество выполненных задач будет зависеть от количества параллельно выполняемых операций. Например, если задать для Parallel.ForEachAsync опцию
то будут выполнены только 3 задачи.
Чтобы этого избежать, можно внутри задачи сохранять выброшенные исключения:
Источник: https://www.meziantou.net/getting-all-exceptions-thrown-from-parallel-foreachasync.htm
Получаем Все Исключения из Parallel.ForEachAsync
На C# вы можете написать асинхронный код, похожий на синхронный код. Люди, использующие async/await, ожидают, что смогут перехватывать исключения, как в синхронном коде. Это очень удобно, но иногда вам нужно получить все исключения из асинхронного метода.
try
{
// Это выбросит несколько исключений
await Parallel.ForEachAsync(
Enumerable.Range(1, 10),
async (i, _) =>
{
throw new Exception(i.ToString());
});
}
// Обработает только первое исключение
// Остальные потеряются
catch (Exception ex)
{
// Выведет случайное число от 1 до 10,
// в зависимости от порядка обработки
Console.WriteLine(ex.ToString());
}
Если вы хотите получить все исключения, необходимо использовать Task.Exceptions или метод, предоставляющий содержимое этого метода, например Task.Wait.
try
{
await Parallel.ForEachAsync(
Enumerable.Range(1, 10),
async (i, _) =>
{
throw new Exception(i.ToString());
}).WithAggregateException();
}
catch (Exception ex)
{
// Выведет все исключения,
// т.к. ex - это AggregateException
Console.WriteLine(ex.ToString());
}
internal static class TaskExtensions
{
public static async Task
WithAggregateException(this Task task)
{
// Отключаем выброс исключений,
// Они будут обработаны в task.Wait()
await task.ConfigureAwait(
ConfigureAwaitOptions.SuppressThrowing);
// Задача уже выполнена, поэтому Wait только
// выбросит AggregateException при ошибке
task.Wait();
}
}
Заметьте, что Parallel.ForEachAsync прекращает обработку элементов из коллекции после выброса первого исключения. Он ожидает завершения текущих задач, а затем сообщает об исключениях. Количество выполненных задач будет зависеть от количества параллельно выполняемых операций. Например, если задать для Parallel.ForEachAsync опцию
var options = new ParallelOptions() {
MaxDegreeOfParallelism = 3
};
то будут выполнены только 3 задачи.
Чтобы этого избежать, можно внутри задачи сохранять выброшенные исключения:
var exceptions = new ConcurrentBag<Exception>();
await Parallel.ForEachAsync(
Enumerable.Range(1, 10),
async (i, _) =>
{
try
{
throw new Exception(i.ToString());
}
catch (Exception ex)
{
exceptions.Add(ex);
}
});
if (!exceptions.IsEmpty)
throw new AggregateException(exceptions);
Источник: https://www.meziantou.net/getting-all-exceptions-thrown-from-parallel-foreachasync.htm
👍25
День 1857. #Холивар
Возник у нас тут небольшой спор с Кириллом, автором канала C# Heppard (кстати, подписывайтесь). Кирилл пишет:
И ещё несколько лет назад, я бы тоже посмеялся над мужиком. А тут… В общем, не знаю почему, но решил я выступить адвокатом дьявола)))
Тут надо оговориться, что Кирилл в основном занимается оптимизациями, а я всё-таки фуллстек, и экономия байтиков и наносекунд для меня скорей теоретическая область и «чисто по фану». То есть, я практически этим не занимаюсь, т.к. в моей практике я чаще оптимизирую запросы к БД или UI в браузере. Но…
В принципе, мужик не так уж и не прав. Понятно, что нет серебряной пули, и у всех ситуации разные. Но, как мне кажется, в довольно приличном количестве случаев апгрейд железа – действительно выход. Понятно, что мы - программисты, мы все любим чистый и быстрый код. Хлебом нас не корми, дай реализовать свой супербыстрый и эффективный по памяти вариант сортировки или словаря.
Но давайте перенесёмся на самый высокий уровень – уровень бизнеса. Как говорит Марк Симан: «Не забывайте, что в конечном итоге, вы делаете продукт, который должен решать задачи бизнеса». Так вот, с точки зрения бизнеса, оптимизировать алгоритм или докупить памяти – разницы нет. Всё выражается в деньгах. Надо заплатить за одно или за другое. Если ПМ, техлид или другой менеджер этого уровня прикинет, как говорится, писю к носу, и сравнит, сколько денег будет стоить апгрейд железа, и сколько примерно надо потратить человекочасов (умноженных на часовую ставку программистов) на оптимизацию, то вполне может оказаться, что «докупить плашку памяти» просто выгоднее для бизнеса (по крайней мере, на ближайшую перспективу).
Конечно, тут будет множество нюансов, особенно если дело касается облачных решений. Там просто так памяти не докинешь, это будут размазанные по времени постоянные издержки, и конечно, через какое-то время оптимизация окажется дешевле. А если мы говорим о микросервисах, кубернетисах и вот этом вот всём, то там ограничения на железо ещё более строгие.
В общем, давайте обсудим. Что думаете? Апгрейд железа может быть опцией? Бывали ли у вас в практике такие случаи?
Возник у нас тут небольшой спор с Кириллом, автором канала C# Heppard (кстати, подписывайтесь). Кирилл пишет:
«Я всегда вспоминаю один диалог. Состоялся он, ох, уж сколько лет прошло, на одном локальном митапе. Я, как всегда, вещал про то, что было бы неплохо писать оптимальный код, в том смысле, чтобы экономить байтики и наносекунды. То да сё, слайды, смешки, лёгкое непонимание в глазах слушателей. Наконец, секция вопросов и ответов.
Я хорошо помню этого мужика, который во время доклада делал фейс-палмы, громко ёрзал стулом, сидел в телефоне. Я ждал вопросов от него и дождался.
- Скажите, а зачем всё это?
- В смысле, - не понимаю я.
- В том смысле, - сказал он авторитетно, - что это всё не нужно. Проще купить плашку в 16 ГБ памяти, чем всё это знать.»
И ещё несколько лет назад, я бы тоже посмеялся над мужиком. А тут… В общем, не знаю почему, но решил я выступить адвокатом дьявола)))
Тут надо оговориться, что Кирилл в основном занимается оптимизациями, а я всё-таки фуллстек, и экономия байтиков и наносекунд для меня скорей теоретическая область и «чисто по фану». То есть, я практически этим не занимаюсь, т.к. в моей практике я чаще оптимизирую запросы к БД или UI в браузере. Но…
В принципе, мужик не так уж и не прав. Понятно, что нет серебряной пули, и у всех ситуации разные. Но, как мне кажется, в довольно приличном количестве случаев апгрейд железа – действительно выход. Понятно, что мы - программисты, мы все любим чистый и быстрый код. Хлебом нас не корми, дай реализовать свой супербыстрый и эффективный по памяти вариант сортировки или словаря.
Но давайте перенесёмся на самый высокий уровень – уровень бизнеса. Как говорит Марк Симан: «Не забывайте, что в конечном итоге, вы делаете продукт, который должен решать задачи бизнеса». Так вот, с точки зрения бизнеса, оптимизировать алгоритм или докупить памяти – разницы нет. Всё выражается в деньгах. Надо заплатить за одно или за другое. Если ПМ, техлид или другой менеджер этого уровня прикинет, как говорится, писю к носу, и сравнит, сколько денег будет стоить апгрейд железа, и сколько примерно надо потратить человекочасов (умноженных на часовую ставку программистов) на оптимизацию, то вполне может оказаться, что «докупить плашку памяти» просто выгоднее для бизнеса (по крайней мере, на ближайшую перспективу).
Конечно, тут будет множество нюансов, особенно если дело касается облачных решений. Там просто так памяти не докинешь, это будут размазанные по времени постоянные издержки, и конечно, через какое-то время оптимизация окажется дешевле. А если мы говорим о микросервисах, кубернетисах и вот этом вот всём, то там ограничения на железо ещё более строгие.
В общем, давайте обсудим. Что думаете? Апгрейд железа может быть опцией? Бывали ли у вас в практике такие случаи?
👍39
Что говорит enum с именем во множественном числе и атрибутом Flags о своих значениях?
#Quiz #BestPractices
#Quiz #BestPractices
Anonymous Quiz
75%
Значения могут комбинироваться как значения битового поля
8%
Значения должны быть степенью двойки
9%
Значения должны обозначать сущности во множественном числе
7%
Значения представляют взаимоисключающие варианты
👍10👎3
День 1858. #Шпаргалка
Примеры Кода для Повседневных Задач
В этой серии представлю вам коллекцию фрагментов кода C#, охватывающих широкий спектр сценариев, с которыми вы можете столкнуться при разработке ПО.
IV. Потоки
Многопоточная обработка — важный аспект параллельного программирования на C#. Рассмотрим, как работать с потоками, используя пространство имен System.Threading.
1. Создание нового потока
Чтобы создать новый поток в C#, вы можете использовать класс Thread:
2. Операции над потоком
- Запуск
- Слияние (ожидание завершения потока)
- Приостановка (текущий поток)
См. подробнее о потоках
3. Использование пула потоков
Для работы с пулом потоков используется класс ThreadPool:
См. подробнее о пуле потоков
V. Задачи
1. Создание новой задачи
Для создания и запуска задачи используется класс Task:
См. подробнее о задачах
2. Ожидание задачи
3. Отмена задачи
Для отмены задачи в C# используется класс CancellationTokenSource, создающий токен отмены. А для собственно отмены – метод токена Cancel():
См. подробнее «Скоординированная отмена»
Источник: https://medium.com/bytehide/100-csharp-code-snippets-for-everyday-problems-e913c786dec9
Примеры Кода для Повседневных Задач
В этой серии представлю вам коллекцию фрагментов кода C#, охватывающих широкий спектр сценариев, с которыми вы можете столкнуться при разработке ПО.
IV. Потоки
Многопоточная обработка — важный аспект параллельного программирования на C#. Рассмотрим, как работать с потоками, используя пространство имен System.Threading.
1. Создание нового потока
Чтобы создать новый поток в C#, вы можете использовать класс Thread:
using System.Threading;
void PrintNumbers()
{
for (int i = 1; i <= 5; i++)
Console.WriteLine(i);
}
var thread = new Thread(PrintNumbers);
2. Операции над потоком
- Запуск
thread.Start();
- Слияние (ожидание завершения потока)
thread.Join();
- Приостановка (текущий поток)
Thread.Sleep(1000); // пауза на 1 секунду
См. подробнее о потоках
3. Использование пула потоков
Для работы с пулом потоков используется класс ThreadPool:
using System.Threading;
ThreadPool.QueueUserWorkItem(PrintNumbers);
См. подробнее о пуле потоков
V. Задачи
1. Создание новой задачи
Для создания и запуска задачи используется класс Task:
using System.Threading.Tasks;
Task.Run(PrintNumbers);
См. подробнее о задачах
2. Ожидание задачи
var task = Task.Run(PrintNumbers);
task.Wait();
3. Отмена задачи
Для отмены задачи в C# используется класс CancellationTokenSource, создающий токен отмены. А для собственно отмены – метод токена Cancel():
using System.Threading;
using System.Threading.Tasks;
var cts = new CancellationTokenSource();
Task.Run(() => PrintNumbers(), cts.Token);
cts.Cancel();
См. подробнее «Скоординированная отмена»
Источник: https://medium.com/bytehide/100-csharp-code-snippets-for-everyday-problems-e913c786dec9
👍14
День 1859. #ЗаметкиНаПолях
Как Принудить Запускать Приложение от Имени Администратора
Чтобы заставить приложение запускаться в Windows от имени администратора, нужно добавить и изменить файл манифеста приложения. Манифест приложения — это XML-файл, который предоставляет информацию о приложении операционной системе.
Начнем с создания консольного приложения:
После этого в свойствах проекта добавим целевую ОС Windows.
Также нужно включить в проект файл app.manifest. В Visual Studio это можно сделать, щелкнув правой кнопкой мыши проект, выбрав Add > New Element… (Добавить > Новый элемент…). В появившемся окне найти Application Manifest file (Windows only) (Файл Манифеста приложения (только Windows)).
В файле app.manifest измените:
на:
Это заставит приложение требовать административных привилегий.
Теперь проверим административные привилегии в файле Program.cs:
Класс WindowsIdentity представляет личность пользователя Windows. WindowsIdentity.GetCurrent() получает личность пользователя, исполняющего текущий процесс, т.е. пользователя, запустившего приложение.
Объект WindowsPrincipal создаётся на основании WindowsIdentity и представляет собой контекст безопасности пользователя, что позволяет проверить его роль.
Теперь можно запустить приложение. В зависимости от ваших настроек Windows, вам либо будет предложено повысить привилегии и запустить приложение от имени администратора, либо приложение будет запущено, но выдаст ошибку:
Unhandled exception: System.ComponentModel.Win32Exception (740):
An error occurred trying to start process
'C:\...\AdministratorApp.exe' ….
The requested operation requires elevation.
Возникает исключение System.ComponentModel.Win32Exception с кодом ошибки 740 «Запрошенная операция требует повышения прав». Подразумевается, что мы можем запускать наше приложение только с правами администратора.
Если запустить приложение от имени администратора, мы увидим в консоли вывод:
Источник: https://code-maze.com/csharp-how-to-force-run-net-application-as-administrator/
Как Принудить Запускать Приложение от Имени Администратора
Чтобы заставить приложение запускаться в Windows от имени администратора, нужно добавить и изменить файл манифеста приложения. Манифест приложения — это XML-файл, который предоставляет информацию о приложении операционной системе.
Начнем с создания консольного приложения:
dotnet new console -n AdministratorApp
После этого в свойствах проекта добавим целевую ОС Windows.
<TargetFramework>net8.0-windows</TargetFramework>
Также нужно включить в проект файл app.manifest. В Visual Studio это можно сделать, щелкнув правой кнопкой мыши проект, выбрав Add > New Element… (Добавить > Новый элемент…). В появившемся окне найти Application Manifest file (Windows only) (Файл Манифеста приложения (только Windows)).
В файле app.manifest измените:
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
на:
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
Это заставит приложение требовать административных привилегий.
Теперь проверим административные привилегии в файле Program.cs:
using System.Security.Principal;
var p = new WindowsPrincipal(
WindowsIdentity.GetCurrent());
if (p.IsInRole(WindowsBuiltInRole.Administrator))
Console.WriteLine("Admin mode");
else
Console.WriteLine("Not Admin mode");
Класс WindowsIdentity представляет личность пользователя Windows. WindowsIdentity.GetCurrent() получает личность пользователя, исполняющего текущий процесс, т.е. пользователя, запустившего приложение.
Объект WindowsPrincipal создаётся на основании WindowsIdentity и представляет собой контекст безопасности пользователя, что позволяет проверить его роль.
Теперь можно запустить приложение. В зависимости от ваших настроек Windows, вам либо будет предложено повысить привилегии и запустить приложение от имени администратора, либо приложение будет запущено, но выдаст ошибку:
Unhandled exception: System.ComponentModel.Win32Exception (740):
An error occurred trying to start process
'C:\...\AdministratorApp.exe' ….
The requested operation requires elevation.
Возникает исключение System.ComponentModel.Win32Exception с кодом ошибки 740 «Запрошенная операция требует повышения прав». Подразумевается, что мы можем запускать наше приложение только с правами администратора.
Если запустить приложение от имени администратора, мы увидим в консоли вывод:
Admin mode
Источник: https://code-maze.com/csharp-how-to-force-run-net-application-as-administrator/
👍20👎1
День 1860. #ЗаметкиНаПолях
Используем HttpContext.Items для Передачи Данных в ASP.NET Core
HttpContext.Items — это словарь, к которому может получить доступ каждый компонент, участвующий в обработке HTTP-запроса. В отличие от HttpContext.Session, данные, сохранённые в HttpContext.Items, не сохраняются между запросами. Это изолирует данные и гарантирует, что они не будут мешать другим одновременным запросам.
В большинстве веб-приложений компонентам часто необходимо взаимодействовать друг с другом. Для этого должна быть организована передача параметров или общее состояние. Но это имеет определённые недостатки и проблемы: проблемы параллелизма, нарушение целостности данных, жёсткая связанность, издержки производительности и т. д.
HttpContext.Items позволяет промежуточному ПО, фильтрам и контроллерам обмениваться данными без прямой связи при их выполнении на разных этапах жизненного цикла запроса. Можно использовать любой тип данных для ключа. Однако, когда мы используем ключ строкового типа, мы рискуем столкнуться с коллизией ключей. Лучшая альтернатива — использовать объект в качестве ключа элемента.
Посмотрим, как использовать это в приложении веб-API ASP.NET Core.
1. Промежуточное ПО
Мы создаём новый экземпляр объекта для ключа MWKey. В методе Invoke() устанавливаем значение в словаре HttpContext.Items.
В Program.cs используем наше промежуточное ПО:
2. Фильтр
Зарегистрируем фильтр в сервисах:
3. В контроллере
Внутри метода контроллера мы используем TryGetValue() для получения значения, установленного в промежуточном ПО по ключу MWKey.
Итого
HttpContext.Items позволяет эффективно использовать данные между компонентами обработки запроса и автоматически удаляет сохранённые данные после завершения запроса.
Полный код примера на GitHub.
Источник: https://code-maze.com/aspnetcore-httpcontext-items-pass-data/
Используем HttpContext.Items для Передачи Данных в ASP.NET Core
HttpContext.Items — это словарь, к которому может получить доступ каждый компонент, участвующий в обработке HTTP-запроса. В отличие от HttpContext.Session, данные, сохранённые в HttpContext.Items, не сохраняются между запросами. Это изолирует данные и гарантирует, что они не будут мешать другим одновременным запросам.
В большинстве веб-приложений компонентам часто необходимо взаимодействовать друг с другом. Для этого должна быть организована передача параметров или общее состояние. Но это имеет определённые недостатки и проблемы: проблемы параллелизма, нарушение целостности данных, жёсткая связанность, издержки производительности и т. д.
HttpContext.Items позволяет промежуточному ПО, фильтрам и контроллерам обмениваться данными без прямой связи при их выполнении на разных этапах жизненного цикла запроса. Можно использовать любой тип данных для ключа. Однако, когда мы используем ключ строкового типа, мы рискуем столкнуться с коллизией ключей. Лучшая альтернатива — использовать объект в качестве ключа элемента.
Посмотрим, как использовать это в приложении веб-API ASP.NET Core.
1. Промежуточное ПО
public class CustomMW
{
private RequestDelegate _next;
public static readonly object MWKey = new();
public CustomMW(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext ctx)
{
ctx.Items[MWKey] = "MW";
await _next(ctx);
}
}
Мы создаём новый экземпляр объекта для ключа MWKey. В методе Invoke() устанавливаем значение в словаре HttpContext.Items.
В Program.cs используем наше промежуточное ПО:
…
app.UseMiddleware<CustomMiddleware>();
app.Run()
2. Фильтр
public class CustomFilter : IActionFilter
{
public static readonly object
FilterKey = new();
…
public void OnActionExecuting(
ActionExecutingContext ctx)
{
ctx.HttpContext.Items[FilterKey] = "Loading…";
…
}
public void OnActionExecuted(
ActionExecutedContext ctx) {
ctx.HttpContext.Items[FilterKey] = "Loaded.";
…
}
…
}
Зарегистрируем фильтр в сервисах:
builder.Services.AddScoped<CustomFilter>();
3. В контроллере
[ServiceFilter(typeof(CustomFilter))]
public IEnumerable<Item> Get()
{
HttpContext.Items.TryGetValue(
CustomMW.MWKey, out var mwValue);
…
}
Внутри метода контроллера мы используем TryGetValue() для получения значения, установленного в промежуточном ПО по ключу MWKey.
Итого
HttpContext.Items позволяет эффективно использовать данные между компонентами обработки запроса и автоматически удаляет сохранённые данные после завершения запроса.
Полный код примера на GitHub.
Источник: https://code-maze.com/aspnetcore-httpcontext-items-pass-data/
👍17
День 1861. #ВопросыНаСобеседовании #Многопоточность
Самые часто задаваемые вопросы на собеседовании по C#
29. Какие потенциальные проблемы могут возникнуть при использовании Thread.Abort() для завершения работающего потока? Объясните последствия и предложите альтернативные методы корректной остановки потока.
Использование Thread.Abort() для завершения работающего потока может привести к нескольким потенциальным проблемам:
1. Непредсказуемое состояние.
Thread.Abort() вызывает исключение ThreadAbortException, которое немедленно прерывает поток, потенциально оставляя общие ресурсы, структуры данных или критические разделы в несогласованном состоянии.
2. Утечки ресурсов.
Если прерванный поток выделил ресурсы, такие как дескрипторы, файловые потоки или соединения с базой данных, они могут не быть освобождены, что приведёт к утечке ресурсов.
3. Взаимные блокировки.
Прерванные потоки, удерживающие блокировки или другие примитивы синхронизации, могут не иметь возможности их освободить, что приводит к взаимоблокировкам в других потоках.
4. Обработка ThreadAbortException.
Если поток перехватывает ThreadAbortException и игнорирует его, запрос на прерывание завершится неудачно, и поток продолжит выполнение.
5. Устарело и больше не поддерживается.
Метод Thread.Abort() не поддерживается в .NET Core, .NET 5 и более поздних версиях, что указывает на то, что его не следует использовать в современных приложениях.
Чтобы корректно остановить поток, есть следующие альтернативы.
1. Общий флаг
Добавим логический флаг, который будет периодически проверяться выполняемым потоком. Если флаг установлен в true, поток должен завершиться. Обязательно использовать ключевое слово volatile или класс Interlocked, чтобы обеспечить правильную синхронизацию:
2. Токен отмены
Если использовать задачи вместо потоков, библиотека параллельных задач (TPL) предоставляет модель отмены с использованием токена отмены:
Грамотная остановка потоков с помощью этих методов гарантирует освобождение ресурсов, правильное управление блокировками и сохранение согласованного состояния общих данных. Она также совместима с современными версиями .NET и TPL.
Источник: https://dev.to/bytehide/c-multithreading-interview-questions-and-answers-4opj
Самые часто задаваемые вопросы на собеседовании по C#
29. Какие потенциальные проблемы могут возникнуть при использовании Thread.Abort() для завершения работающего потока? Объясните последствия и предложите альтернативные методы корректной остановки потока.
Использование Thread.Abort() для завершения работающего потока может привести к нескольким потенциальным проблемам:
1. Непредсказуемое состояние.
Thread.Abort() вызывает исключение ThreadAbortException, которое немедленно прерывает поток, потенциально оставляя общие ресурсы, структуры данных или критические разделы в несогласованном состоянии.
2. Утечки ресурсов.
Если прерванный поток выделил ресурсы, такие как дескрипторы, файловые потоки или соединения с базой данных, они могут не быть освобождены, что приведёт к утечке ресурсов.
3. Взаимные блокировки.
Прерванные потоки, удерживающие блокировки или другие примитивы синхронизации, могут не иметь возможности их освободить, что приводит к взаимоблокировкам в других потоках.
4. Обработка ThreadAbortException.
Если поток перехватывает ThreadAbortException и игнорирует его, запрос на прерывание завершится неудачно, и поток продолжит выполнение.
5. Устарело и больше не поддерживается.
Метод Thread.Abort() не поддерживается в .NET Core, .NET 5 и более поздних версиях, что указывает на то, что его не следует использовать в современных приложениях.
Чтобы корректно остановить поток, есть следующие альтернативы.
1. Общий флаг
Добавим логический флаг, который будет периодически проверяться выполняемым потоком. Если флаг установлен в true, поток должен завершиться. Обязательно использовать ключевое слово volatile или класс Interlocked, чтобы обеспечить правильную синхронизацию:
volatile bool stopRequested = false;
var thread = new Thread(() => {
while (!stopRequested)
{
// выполняем работу
// ...
}
});
// Останавливаем поток
stopRequested = true;
2. Токен отмены
Если использовать задачи вместо потоков, библиотека параллельных задач (TPL) предоставляет модель отмены с использованием токена отмены:
var cts = new CancellationTokenSource();
var task = Task.Run(() =>
{
while (!cts.IsCancellationRequested)
{
// Выполняем работу
// ...
}
});
// Останавливаем задачу
cts.Cancel();
Грамотная остановка потоков с помощью этих методов гарантирует освобождение ресурсов, правильное управление блокировками и сохранение согласованного состояния общих данных. Она также совместима с современными версиями .NET и TPL.
Источник: https://dev.to/bytehide/c-multithreading-interview-questions-and-answers-4opj
👍22
День 1862. #ЗаметкиНаПолях
REST vs RESTful. В чём Разница? Начало
REST API — один из самых популярных API в сообществе веб-разработчиков. В чем разница между REST API и RESTful API?
Короткий ответ: REST - REpresentational State Transfer (Передача Репрезентативного Состояния). Это архитектурный шаблон для создания веб-сервисов. RESTful-сервис реализует этот шаблон.
Длинный ответ начинается со слов «вроде как» и «это зависит» и продолжается более полными определениями.
REST
Для некоторых REST – это обмен JSON-данными между клиентом и сервером. Это не только не полное определение, но и не всегда верное. Спецификация REST не требует HTTP или JSON, JSON или XML там даже не упоминаются.
Рой Филдинг представил архитектурный шаблон REST в своей диссертации в 2000 году. Там описывается метод облегчения обмена данными приложений между клиентами и серверами. Ключевой особенностью является то, что от клиента не требуется никаких предварительных знаний о приложении. Филдинг не предъявляет особых требований. Вместо этого он определяет REST в отношении ограничений и архитектурных элементов.
Архитектурные ограничения REST
1. Клиент-сервер
REST-сервисы имеют сервер, который управляет данными и состоянием приложения. Сервер взаимодействует с клиентом, который обрабатывает взаимодействие с пользователем. Чёткое разделение задач сервера и клиента означает, что вы можете обновлять и улучшать их независимо.
2. Нет сохранения состояния
Сервер не поддерживает состояние клиента. Клиенты управляют состоянием своего приложения. Их запросы к серверу содержат всю информацию, необходимую для их обработки.
3. Кэш
Сервер должен помечать свои ответы как кэшируемые или нет. Так инфраструктура и клиент могут кэшировать ответы, когда это возможно, для повышения производительности. Некэшируемая информация может удаляться, так что ни один клиент не будет использовать устаревшие данные.
4. Единый интерфейс
Филдинг говорит: «Главной особенностью, которая отличает архитектурный стиль REST от других сетевых стилей, является акцент на единообразном интерфейсе между компонентами». REST-сервисы предоставляют данные в виде ресурсов с согласованным пространством имён.
5. Многоуровневая система
Компоненты системы не могут «видеть» ничего за пределами своего уровня. Так вы можете легко добавить балансировщики нагрузки и прокси-серверы для повышения безопасности или производительности.
RESTful-сервис — это больше, чем веб-сервер, который обменивается JSON или любыми другими форматами данных. Вместе приведённые выше ограничения создают очень специфический тип приложения.
Применение ограничений
Клиент-серверность, многоуровневость системы и ограничение на несохранение состояния образуют приложение с чёткими границами и чётким разделением задач. По запросу данные передаются с сервера клиенту, где они отображаются или обрабатываются. Если необходимы какие-либо изменения в состоянии, клиент отправляет изменённые данные обратно на сервер для хранения. В REST и клиент, и сервер обладают общими знаниями о данных и их состоянии. Архитектура не скрывает данные, она лишь скрывает реализацию. Данные приложения доступны клиентам в понятном и согласованном интерфейсе и, по возможности, кэшируются.
Окончание следует…
Источник: https://blog.ndepend.com/rest-vs-restful/
REST vs RESTful. В чём Разница? Начало
REST API — один из самых популярных API в сообществе веб-разработчиков. В чем разница между REST API и RESTful API?
Короткий ответ: REST - REpresentational State Transfer (Передача Репрезентативного Состояния). Это архитектурный шаблон для создания веб-сервисов. RESTful-сервис реализует этот шаблон.
Длинный ответ начинается со слов «вроде как» и «это зависит» и продолжается более полными определениями.
REST
Для некоторых REST – это обмен JSON-данными между клиентом и сервером. Это не только не полное определение, но и не всегда верное. Спецификация REST не требует HTTP или JSON, JSON или XML там даже не упоминаются.
Рой Филдинг представил архитектурный шаблон REST в своей диссертации в 2000 году. Там описывается метод облегчения обмена данными приложений между клиентами и серверами. Ключевой особенностью является то, что от клиента не требуется никаких предварительных знаний о приложении. Филдинг не предъявляет особых требований. Вместо этого он определяет REST в отношении ограничений и архитектурных элементов.
Архитектурные ограничения REST
1. Клиент-сервер
REST-сервисы имеют сервер, который управляет данными и состоянием приложения. Сервер взаимодействует с клиентом, который обрабатывает взаимодействие с пользователем. Чёткое разделение задач сервера и клиента означает, что вы можете обновлять и улучшать их независимо.
2. Нет сохранения состояния
Сервер не поддерживает состояние клиента. Клиенты управляют состоянием своего приложения. Их запросы к серверу содержат всю информацию, необходимую для их обработки.
3. Кэш
Сервер должен помечать свои ответы как кэшируемые или нет. Так инфраструктура и клиент могут кэшировать ответы, когда это возможно, для повышения производительности. Некэшируемая информация может удаляться, так что ни один клиент не будет использовать устаревшие данные.
4. Единый интерфейс
Филдинг говорит: «Главной особенностью, которая отличает архитектурный стиль REST от других сетевых стилей, является акцент на единообразном интерфейсе между компонентами». REST-сервисы предоставляют данные в виде ресурсов с согласованным пространством имён.
5. Многоуровневая система
Компоненты системы не могут «видеть» ничего за пределами своего уровня. Так вы можете легко добавить балансировщики нагрузки и прокси-серверы для повышения безопасности или производительности.
RESTful-сервис — это больше, чем веб-сервер, который обменивается JSON или любыми другими форматами данных. Вместе приведённые выше ограничения создают очень специфический тип приложения.
Применение ограничений
Клиент-серверность, многоуровневость системы и ограничение на несохранение состояния образуют приложение с чёткими границами и чётким разделением задач. По запросу данные передаются с сервера клиенту, где они отображаются или обрабатываются. Если необходимы какие-либо изменения в состоянии, клиент отправляет изменённые данные обратно на сервер для хранения. В REST и клиент, и сервер обладают общими знаниями о данных и их состоянии. Архитектура не скрывает данные, она лишь скрывает реализацию. Данные приложения доступны клиентам в понятном и согласованном интерфейсе и, по возможности, кэшируются.
Окончание следует…
Источник: https://blog.ndepend.com/rest-vs-restful/
👍20
День 1863. #ЗаметкиНаПолях
REST vs RESTful. В чём Разница? Окончание
Начало
RPC через HTTP vs RESTful
Часто говорят, что сервис «не REST», из-за неправильных URI или использования глаголов HTTP. Имеется в виду представление данных REST как единообразного набора ресурсов.
Это различие иногда формулируется как различие между вызовами удаленных процедур (RPC) и REST. Представьте себе веб-сервис для перечисления, добавления и удаления товаров онлайн-магазина.
В одной версии есть один URL, который мы запрашиваем с помощью HTTP GET или POST. Вы взаимодействуете с сервисом, отправляя данные POST, содержащие то, что вы хотите сделать. Например, добавить элемент с помощью NewItem:
Запросить через POST и ItemRequest:
Некоторые реализации также допускают запросы через GET-параметры:
И т.п.
Это не REST. Мы не обмениваемся состоянием ресурсов. Мы вызываем функцию с аргументами, которые находятся в документе JSON или параметрая URL.
RESTful-сервис имеет URI для каждого ресурса. Поэтому добавление нового товара будет выглядеть так же, как в примере выше. Но на этом сходства заканчиваются.
Запрос элемента – это всегда GET:
Удаление - DELETE:
Изменение - PUT:
Разница важна. В REST операции используют различные глаголы HTTP, которые соответствуют действиям с данными: GET, POST, PUT, DELETE и PATCH имеют определённые контракты. Большинство хорошо спроектированных REST API также возвращают определённые коды HTTP в зависимости от результата запроса. Основным различием является то, что URI соответствуют данным, а не удалёнными методам.
REST против RESTful и модель зрелости Ричардсона
Разработка URI в соответствии с ресурсами и использование глаголов HTTP способствует предсказуемости API. Когда разработчики поймут, как вы структурировали ресурсы, они смогут сделать обоснованные прогнозы относительно структуры API. В этом смысле основное внимание уделяется пониманию самих данных, а не связанных с ними операций.
Но даже если вы не можете сделать API полностью предсказуемым, вы можете документировать любой REST-сервис с помощью HATEOAS. Тогда каждый элемент, возвращаемый сервером, будет содержать ссылки для удаления или изменения ресурса.
Многие сайты не соответствуют этому требованию, но по-прежнему называются REST. Леонард Ричардсон создал модель уровней зрелости REST:
0 – API через HTTP с вызовом удалённых методов с аргументами.
1 – Использование ресурсов вместо методов.
2 – Правильное использование HTTP-глаголов.
3 – Использование HATEOAS, что делает видимым весь API или его часть.
По Филдингу требуется третий уровень, так что большинство приложений не являются REST.
То, насколько хорошо архитектура соответствует стандарту, не так важно, как то, насколько хорошо она соответствует потребностям и может расти вместе с бизнесом.
Архитектурный шаблон REST имеет множество преимуществ. Филдинг разработал его для Интернета, и 18 лет спустя большинство ограничений, которые он добавил, все ещё с нами. В 2000 году у нас не было Android или iPhone, а IE5 занимал 50% рынка браузеров. Но Филдинг понимал, что нужно онлайн-приложениям и как веб-клиенты будут развиваться от механизмов отображения HTML в полноценные приложения. Инструменты, которые мы используем сегодня, адаптированы к REST, а не наоборот.
Модель зрелости Ричардсона — хорошее руководство для разработки приложений. Желательно находиться на втором уровне модели и посмотреть, как третий уровень может улучшить ваш дизайн.
Источник: https://blog.ndepend.com/rest-vs-restful/
REST vs RESTful. В чём Разница? Окончание
Начало
RPC через HTTP vs RESTful
Часто говорят, что сервис «не REST», из-за неправильных URI или использования глаголов HTTP. Имеется в виду представление данных REST как единообразного набора ресурсов.
Это различие иногда формулируется как различие между вызовами удаленных процедур (RPC) и REST. Представьте себе веб-сервис для перечисления, добавления и удаления товаров онлайн-магазина.
В одной версии есть один URL, который мы запрашиваем с помощью HTTP GET или POST. Вы взаимодействуете с сервисом, отправляя данные POST, содержащие то, что вы хотите сделать. Например, добавить элемент с помощью NewItem:
POST /inventory HTTP/1.1
{
"NewItem": {
"name": "new item",
"price": "9.99",
"id": "1001"
}
}
Запросить через POST и ItemRequest:
POST /inventory HTTP/1.1
{
"ItemRequest": {
"id": "1001"
}
}
Некоторые реализации также допускают запросы через GET-параметры:
POST /inventory?id=1001 HTTP/1.1
И т.п.
Это не REST. Мы не обмениваемся состоянием ресурсов. Мы вызываем функцию с аргументами, которые находятся в документе JSON или параметрая URL.
RESTful-сервис имеет URI для каждого ресурса. Поэтому добавление нового товара будет выглядеть так же, как в примере выше. Но на этом сходства заканчиваются.
Запрос элемента – это всегда GET:
GET /item/1001 HTTP/1.1
Удаление - DELETE:
DELETE /item/1001 HTTP/1.1
Изменение - PUT:
PUT /inventory HTTP/1.1
{
"Item": {
"name": "new item",
"price": "7.99",
"id": "1001"
}
}
Разница важна. В REST операции используют различные глаголы HTTP, которые соответствуют действиям с данными: GET, POST, PUT, DELETE и PATCH имеют определённые контракты. Большинство хорошо спроектированных REST API также возвращают определённые коды HTTP в зависимости от результата запроса. Основным различием является то, что URI соответствуют данным, а не удалёнными методам.
REST против RESTful и модель зрелости Ричардсона
Разработка URI в соответствии с ресурсами и использование глаголов HTTP способствует предсказуемости API. Когда разработчики поймут, как вы структурировали ресурсы, они смогут сделать обоснованные прогнозы относительно структуры API. В этом смысле основное внимание уделяется пониманию самих данных, а не связанных с ними операций.
Но даже если вы не можете сделать API полностью предсказуемым, вы можете документировать любой REST-сервис с помощью HATEOAS. Тогда каждый элемент, возвращаемый сервером, будет содержать ссылки для удаления или изменения ресурса.
Многие сайты не соответствуют этому требованию, но по-прежнему называются REST. Леонард Ричардсон создал модель уровней зрелости REST:
0 – API через HTTP с вызовом удалённых методов с аргументами.
1 – Использование ресурсов вместо методов.
2 – Правильное использование HTTP-глаголов.
3 – Использование HATEOAS, что делает видимым весь API или его часть.
По Филдингу требуется третий уровень, так что большинство приложений не являются REST.
То, насколько хорошо архитектура соответствует стандарту, не так важно, как то, насколько хорошо она соответствует потребностям и может расти вместе с бизнесом.
Архитектурный шаблон REST имеет множество преимуществ. Филдинг разработал его для Интернета, и 18 лет спустя большинство ограничений, которые он добавил, все ещё с нами. В 2000 году у нас не было Android или iPhone, а IE5 занимал 50% рынка браузеров. Но Филдинг понимал, что нужно онлайн-приложениям и как веб-клиенты будут развиваться от механизмов отображения HTML в полноценные приложения. Инструменты, которые мы используем сегодня, адаптированы к REST, а не наоборот.
Модель зрелости Ричардсона — хорошее руководство для разработки приложений. Желательно находиться на втором уровне модели и посмотреть, как третий уровень может улучшить ваш дизайн.
Источник: https://blog.ndepend.com/rest-vs-restful/
👍16
День 1864. #ЗаметкиНаПолях
Необязательные Параметры Могут Быть в Середине
В .NET необязательные параметры не всегда должны быть последними параметрами в сигнатуре метода. Хотя C# не позволяет объявлять необязательные параметры в середине, это можно сделать в IL или других языках, таких как VB.NET или F#. Кроме того, компилятор может создавать методы с необязательными параметрами в середине списка параметров.
Почему это имеет значение? Если вы пишете генератор исходного кода, который генерирует новые методы с теми же параметрами, что и другой метод, вам может потребоваться сгенерировать методы с необязательными параметрами в середине списка параметров. Хотя это довольно редкий случай, это допустимо в .NET и может быть полезно в некоторых сценариях.
Чтобы лучше понять суть вопроса, начнём с простого примера. Следующий код недопустим в C#:
При компиляции метода с необязательными параметрами компилятор заменяет синтаксис C# двумя атрибутами: [Optional] и [DefaultParameterValue]. Атрибут [Optional] используется для пометки параметра как необязательного, а атрибут [DefaultParameterValue] используется для установки значения по умолчанию. Следующий код допустим в C#:
Обратите внимание, что компилятор C# более строг, чем в других языках, в отношении допустимых значений в [DefaultParameterValue]. Он гарантирует, что тип значения совпадает с типом параметра, но может не допускать некоторых допустимых преобразований. Например, следующий код будет допустим в VB.NET или F#, но не в C#:
Тот же метод в F# допустим:
Кстати: даже компилятор C# может сгенерировать метод с необязательными параметрами в середине списка параметров. При создании индексатора с необязательными параметрами компилятор генерирует метод с необязательным параметром в середине:
Источник: https://www.meziantou.net/optional-parameters.htm
Необязательные Параметры Могут Быть в Середине
В .NET необязательные параметры не всегда должны быть последними параметрами в сигнатуре метода. Хотя C# не позволяет объявлять необязательные параметры в середине, это можно сделать в IL или других языках, таких как VB.NET или F#. Кроме того, компилятор может создавать методы с необязательными параметрами в середине списка параметров.
Почему это имеет значение? Если вы пишете генератор исходного кода, который генерирует новые методы с теми же параметрами, что и другой метод, вам может потребоваться сгенерировать методы с необязательными параметрами в середине списка параметров. Хотя это довольно редкий случай, это допустимо в .NET и может быть полезно в некоторых сценариях.
Чтобы лучше понять суть вопроса, начнём с простого примера. Следующий код недопустим в C#:
// CS1737 Optional parameters must appear after all required parameters
void Sample(int a = 1, int b)
{
}
При компиляции метода с необязательными параметрами компилятор заменяет синтаксис C# двумя атрибутами: [Optional] и [DefaultParameterValue]. Атрибут [Optional] используется для пометки параметра как необязательного, а атрибут [DefaultParameterValue] используется для установки значения по умолчанию. Следующий код допустим в C#:
// Вывод: 42
Sample(b: 2);
// Объявляем "a" как необязательный
// со значением 40 по умолчанию
void Sample(
[Optional, DefaultParameterValue(40)]
int a,
int b)
{
Console.WriteLine(a + b);
}
Обратите внимание, что компилятор C# более строг, чем в других языках, в отношении допустимых значений в [DefaultParameterValue]. Он гарантирует, что тип значения совпадает с типом параметра, но может не допускать некоторых допустимых преобразований. Например, следующий код будет допустим в VB.NET или F#, но не в C#:
// CS1908 The type of the argument to the DefaultParameterValue attribute must match the parameter type
void Sample(
[Optional, DefaultParameterValue(default)]
CancellationToken cancellationToken)
{
}
Тот же метод в F# допустим:
type Class1 =
static member Sample(
[<Optional; DefaultParameterValue(CancellationToken())>]
ct: CancellationToken) = ()
Кстати: даже компилятор C# может сгенерировать метод с необязательными параметрами в середине списка параметров. При создании индексатора с необязательными параметрами компилятор генерирует метод с необязательным параметром в середине:
// Создаёт метод (set_Item) с необязательным параметром в середине
// int set_Item(int a, int b = 2, int value)
public int this[int a, int b = 2]
{
set { }
}
Источник: https://www.meziantou.net/optional-parameters.htm
👍11👎2
День 1865. #ЗаметкиНаПолях
Безопасность Типов в xUnit с Помощью TheoryData<T>
В xUnit есть небольшая, но очень полезная особенность – класс TheoryData<T>.
Проблема
Имеем следующий код тестов репозитория:
Проблема здесь в том, что данные имеют тип IEnumerable<object[]>, а параметр act — тип Action<IServiceCollection>. То есть теоретически можно передать в метод что угодно, и компилятор с радостью это примет. Вот тут-то и пригодится TheoryData<T>.
Решение
TheoryData<T> — это класс, который позволяет обертывать данные строго типизированным способом. Например:
Точно такой же тест, но более типобезопасный. Теперь компилятор не позволит передать в метод ничего, кроме Action<IServiceCollection>.
Источник: https://steven-giesel.com/blogPost/a8aa3385-8829-444a-b269-7ecb38aeaf2f/typesafety-in-xunit-with-theorydatat
Безопасность Типов в xUnit с Помощью TheoryData<T>
В xUnit есть небольшая, но очень полезная особенность – класс TheoryData<T>.
Проблема
Имеем следующий код тестов репозитория:
public static IEnumerable<object[]>
Data => new List<object[]>
{
new object[] {
new Action<IServiceCollection>(
s => s.UseSqliteAsStorageProvider())
},
new object[] {
new Action<IServiceCollection>(
s => s.UseSqlAsStorageProvider())
},
new object[] {
new Action<IServiceCollection>(
s => s.UseInMemoryAsStorageProvider())
}
};
[Theory]
[MemberData(nameof(Data))]
public void TestStorageProvider(
Action<IServiceCollection> act)
{ … }
Проблема здесь в том, что данные имеют тип IEnumerable<object[]>, а параметр act — тип Action<IServiceCollection>. То есть теоретически можно передать в метод что угодно, и компилятор с радостью это примет. Вот тут-то и пригодится TheoryData<T>.
Решение
TheoryData<T> — это класс, который позволяет обертывать данные строго типизированным способом. Например:
public static TheoryData<Action<IServiceCollection>>
Data => new()
{
s => s.UseSqliteAsStorageProvider(),
s => s.UseSqlAsStorageProvider(),
s => s.UseInMemoryAsStorageProvider()
};
[Theory]
[MemberData(nameof(Data))]
public void TestStorageProvider(
Action<IServiceCollection> act)
{ … }
Точно такой же тест, но более типобезопасный. Теперь компилятор не позволит передать в метод ничего, кроме Action<IServiceCollection>.
Источник: https://steven-giesel.com/blogPost/a8aa3385-8829-444a-b269-7ecb38aeaf2f/typesafety-in-xunit-with-theorydatat
👍21
Когда метод Validate вызывается на объекте, реализующем IValidatableObject, какой возвращается результат?
#Quiz #ASPNET
#Quiz #ASPNET
Anonymous Quiz
14%
Значение True/False, обозначающее, что все свойства прошли валидацию
29%
IEnumerable из объектов ValidationResult с ошибками валидации
53%
Объект ValidationResult, включающий True/False результата валидации и коллекцию ошибок валиации
4%
IEnumerable всех свойств объекта с флагом валидности