День 2497. #ЗаметкиНаПолях
Используем Выражения Коллекций для Пользовательских Типов
В C#12 появились выражения коллекций — новый упрощённый синтаксис для инициализации коллекций:
Этот синтаксис отлично работает со встроенными типами коллекций, но как насчёт наших типов коллекций? Здесь на помощь приходит атрибут CollectionBuilderAttribute, позволяющий расширить этот современный синтаксис на пользовательские типы.
Пользовательские коллекции
Представьте, что вы создали свой тип коллекции:
Раньше вы не могли использовать синтаксис выражений коллекций для этого типа. Вам приходилось использовать старый способ:
Либо (что не сильно меняет дело):
Не слишком элегантно.
Атрибут CollectionBuilderAttribute устраняет этот разрыв, сообщая компилятору, как конструировать вашу коллекцию из выражения коллекции:
Теперь мы можем использовать:
Компилятор автоматически вызовет метод Create, передав ему элементы.
Как это работает
Атрибут принимает два параметра:
1. Тип построителя – тип, содержащий фабричный метод (
2. Имя метода – имя статического метода, создающего экземпляр (
Метод построителя должен:
- быть статическим;
- принимать либо ReadOnlySpan<T> (предпочтительно), либо T[];
- возвращать экземпляр типа коллекции;
- иметь параметры типа, соответствующие вашей коллекции.
Замечание: также необходимо, чтобы коллекция имела «тип итерации», то есть имела метод
Когда всё это реализовано, компилятор может сгенерировать необходимый код для построения вашей коллекции наиболее эффективным способом.
Источник: https://bartwullems.blogspot.com/2025/11/how-to-uninstall-older-net-core-versions.html
Используем Выражения Коллекций для Пользовательских Типов
В C#12 появились выражения коллекций — новый упрощённый синтаксис для инициализации коллекций:
int[] numbers = [1, 2, 3, 4, 5];
List<string> names = ["Alice", "Bob", "Charlie"];
Этот синтаксис отлично работает со встроенными типами коллекций, но как насчёт наших типов коллекций? Здесь на помощь приходит атрибут CollectionBuilderAttribute, позволяющий расширить этот современный синтаксис на пользовательские типы.
Пользовательские коллекции
Представьте, что вы создали свой тип коллекции:
public class MyCollection<T>
{
private readonly List<T> _items;
public MyCollection(ReadOnlySpan<T> items) =>
_items = [.. items];
public IEnumerator<T> GetEnumerator()
=> _items.GetEnumerator();
// другие члены…
}
Раньше вы не могли использовать синтаксис выражений коллекций для этого типа. Вам приходилось использовать старый способ:
var myCol =
new MyCollection<int>(new[] { 1, 2, 3, 4, 5 });
Либо (что не сильно меняет дело):
var myCol =
new MyCollection<int>([1, 2, 3, 4, 5]);
Не слишком элегантно.
Атрибут CollectionBuilderAttribute устраняет этот разрыв, сообщая компилятору, как конструировать вашу коллекцию из выражения коллекции:
[CollectionBuilder(typeof(MyCollectionBuilder),
nameof(MyCollectionBuilder.Create))]
public class MyCollection<T>
{
//…
}
public static class MyCollectionBuilder
{
public static MyCollection<T>
Create<T>(ReadOnlySpan<T> items)
=> new([..items]);
}
Теперь мы можем использовать:
MyCollection<int> myCol = [1, 2, 3, 4, 5];
Компилятор автоматически вызовет метод Create, передав ему элементы.
Как это работает
Атрибут принимает два параметра:
1. Тип построителя – тип, содержащий фабричный метод (
typeof(MyCollectionBuilder));2. Имя метода – имя статического метода, создающего экземпляр (
"Create").Метод построителя должен:
- быть статическим;
- принимать либо ReadOnlySpan<T> (предпочтительно), либо T[];
- возвращать экземпляр типа коллекции;
- иметь параметры типа, соответствующие вашей коллекции.
Замечание: также необходимо, чтобы коллекция имела «тип итерации», то есть имела метод
GetEnumerator(), возвращающий IEnumerator (или IEnumerator<T>). Можно либо реализовать интерфейс IEnumerable или IEnumerable<T>, либо просто добавить метод GetEnumerator().Когда всё это реализовано, компилятор может сгенерировать необходимый код для построения вашей коллекции наиболее эффективным способом.
Источник: https://bartwullems.blogspot.com/2025/11/how-to-uninstall-older-net-core-versions.html
👍20
День 2498. #Architecture
Архитектура Вертикальных Срезов. Где Живёт Общая Логика? Начало
Архитектура Вертикальных Срезов (Vertical Slice Architecture) кажется очень удобной, когда вы с ней впервые сталкиваетесь. Не нужно прыгать между семью уровнями, чтобы добавить одно поле, не нужны десятки проектов в решении. Но при реализации более сложных функций начинают проявляться недостатки.
Допустим, у нас есть срезы CreateOrder, UpdateOrder и GetOrder. Внезапно возникает повторение: логика проверки адреса заказа в 3х местах. А подсчёт подытога, налогов и итога нужен как для корзины, так и для оформления заказа. Хочется создать общий проект или папку SharedServices. Это самый критический момент во внедрении VSA. Выберете неправильный вариант, и снова создадите связанность, от которой пытались избавиться. Выберите правильный - сохраните независимость, которой отличается VSA.
Чистая архитектура устанавливает строгие ограничения. Она точно определяет, где находится код:
- сущности - в домене (Domain),
- интерфейсы — в приложении (Application),
- реализации — в инфраструктуре (Infrastructure).
Это безопасно, предотвращает ошибки, но также препятствует использованию обходных путей, даже когда они уместны.
VSA устраняет ограничения. Она гласит: «Организуйте код по функциям, а не по техническим особенностям». Это обеспечивает скорость и гибкость, но перекладывает бремя дисциплины на вас. Что делать?
Ловушка: «Общий» мусорный ящик
Путь наименьшего сопротивления — создать проект (или папку) с именем Shared, Common или Utils. Это почти всегда ошибка.
Представьте проект Common.Services с классом OrderCalculationService:
- метод для итогов корзины (используется Cart),
- для истории доходов (используется Reporting),
- метод для форматирования счетов (используется Invoices).
3 несвязанные задачи. 3 разные причины изменений. Класс, объединяющий всё.
Общий проект неизбежно становится мусорным ящиком для всего, чему вы не удосужились дать правильное название. Это создает запутанную сеть зависимостей, где несвязанные функции связаны друг с другом, потому что они случайно используют один и тот же вспомогательный метод. Мы вернули связанность, от которой пытались избавиться.
Схема принятия решений
В потенциальной ситуации совместного использования кода нужно задать 3 вопроса:
1. Это инфраструктурный или доменный код?
Инфраструктура (контексты БД, ведение журнала, HTTP-клиенты) почти всегда используются из нескольких мест. Концепции домена требуют более тщательного изучения (об этом позже).
2. Насколько стабильна концепция?
Если меняется раз в год – можно кидать в общий код. Если при каждом запросе на новую функцию, оставьте её локальной.
3. Нарушается ли «правило трёх»?
Дублирование кода 1 раз допустимо. Создание 3х копий должно насторожить. Не абстрагируйтесь, пока не достигнете трёх.
Мы решаем эту проблему рефакторингом кода. Далее рассмотрим несколько примеров.
Продолжение следует…
Источник: https://www.milanjovanovic.tech/blog/vertical-slice-architecture-where-does-the-shared-logic-live
Архитектура Вертикальных Срезов. Где Живёт Общая Логика? Начало
Архитектура Вертикальных Срезов (Vertical Slice Architecture) кажется очень удобной, когда вы с ней впервые сталкиваетесь. Не нужно прыгать между семью уровнями, чтобы добавить одно поле, не нужны десятки проектов в решении. Но при реализации более сложных функций начинают проявляться недостатки.
Допустим, у нас есть срезы CreateOrder, UpdateOrder и GetOrder. Внезапно возникает повторение: логика проверки адреса заказа в 3х местах. А подсчёт подытога, налогов и итога нужен как для корзины, так и для оформления заказа. Хочется создать общий проект или папку SharedServices. Это самый критический момент во внедрении VSA. Выберете неправильный вариант, и снова создадите связанность, от которой пытались избавиться. Выберите правильный - сохраните независимость, которой отличается VSA.
Чистая архитектура устанавливает строгие ограничения. Она точно определяет, где находится код:
- сущности - в домене (Domain),
- интерфейсы — в приложении (Application),
- реализации — в инфраструктуре (Infrastructure).
Это безопасно, предотвращает ошибки, но также препятствует использованию обходных путей, даже когда они уместны.
VSA устраняет ограничения. Она гласит: «Организуйте код по функциям, а не по техническим особенностям». Это обеспечивает скорость и гибкость, но перекладывает бремя дисциплины на вас. Что делать?
Ловушка: «Общий» мусорный ящик
Путь наименьшего сопротивления — создать проект (или папку) с именем Shared, Common или Utils. Это почти всегда ошибка.
Представьте проект Common.Services с классом OrderCalculationService:
- метод для итогов корзины (используется Cart),
- для истории доходов (используется Reporting),
- метод для форматирования счетов (используется Invoices).
3 несвязанные задачи. 3 разные причины изменений. Класс, объединяющий всё.
Общий проект неизбежно становится мусорным ящиком для всего, чему вы не удосужились дать правильное название. Это создает запутанную сеть зависимостей, где несвязанные функции связаны друг с другом, потому что они случайно используют один и тот же вспомогательный метод. Мы вернули связанность, от которой пытались избавиться.
Схема принятия решений
В потенциальной ситуации совместного использования кода нужно задать 3 вопроса:
1. Это инфраструктурный или доменный код?
Инфраструктура (контексты БД, ведение журнала, HTTP-клиенты) почти всегда используются из нескольких мест. Концепции домена требуют более тщательного изучения (об этом позже).
2. Насколько стабильна концепция?
Если меняется раз в год – можно кидать в общий код. Если при каждом запросе на новую функцию, оставьте её локальной.
3. Нарушается ли «правило трёх»?
Дублирование кода 1 раз допустимо. Создание 3х копий должно насторожить. Не абстрагируйтесь, пока не достигнете трёх.
Мы решаем эту проблему рефакторингом кода. Далее рассмотрим несколько примеров.
Продолжение следует…
Источник: https://www.milanjovanovic.tech/blog/vertical-slice-architecture-where-does-the-shared-logic-live
👍13
День 2499. #Architecture
Архитектура Вертикальных Срезов. Где Живёт Общая Логика? Продолжение
Начало
Три уровня общего кода
Вместо бинарного разделения «общий/локальный код» мыслите тремя уровнями.
Уровень 1. Техническая инфраструктура (спокойно используйте общий код)
Чистая инфраструктура, одинаково влияющая на все срезы: логеры, фабрики подключений к БД, промежуточное ПО аутентификации, шаблон Result, конвейеры валидации и т.п. Централизуйте всё это в проекте Shared.Kernel или Infrastructure. Обратите внимание, что это также может быть папка внутри проекта. Такой код редко меняется в связи с бизнес-требованиями.
Уровень 2. Концепции домена (используйте общий код)
Вместо того, чтобы разбрасывать бизнес-правила по срезам, передавайте их в сущности и объекты-значения:
CancelOrder, GetOrder и UpdateOrder все используют одни бизнес-правила. Логика живёт в одном месте. Т.е. разные вертикальные срезы могут использовать одну и ту же модель домена.
Уровень 3: Логика, специфичная для конкретного объекта (сохраняйте её локально)
Логика, общая для связанных срезов, таких как CreateOrder и UpdateOrder, не обязательно должна быть глобальной. Создайте общую папку (из каждого правила есть исключения) внутри функции:
Это также имеет скрытое преимущество. Если вы удалите функцию Orders, общая логика будет удалена вместе с ней. Никакого зомби-кода.
Наконец рассмотрим несколько сложных сценариев, которые большинство упускают из виду.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/vertical-slice-architecture-where-does-the-shared-logic-live
Архитектура Вертикальных Срезов. Где Живёт Общая Логика? Продолжение
Начало
Три уровня общего кода
Вместо бинарного разделения «общий/локальный код» мыслите тремя уровнями.
Уровень 1. Техническая инфраструктура (спокойно используйте общий код)
Чистая инфраструктура, одинаково влияющая на все срезы: логеры, фабрики подключений к БД, промежуточное ПО аутентификации, шаблон Result, конвейеры валидации и т.п. Централизуйте всё это в проекте Shared.Kernel или Infrastructure. Обратите внимание, что это также может быть папка внутри проекта. Такой код редко меняется в связи с бизнес-требованиями.
// Техническое ядро
public readonly record struct Result
{
public bool IsSuccess { get; }
public string Error { get; }
private Result(bool isSuccess, string error)
{
IsSuccess = isSuccess;
Error = error;
}
public static Result Success()
=> new(true, string.Empty);
public static Result Failure(string error)
=> new(false, error);
}
Уровень 2. Концепции домена (используйте общий код)
Вместо того, чтобы разбрасывать бизнес-правила по срезам, передавайте их в сущности и объекты-значения:
// Сущность с бизнес-логикой
public class Order
{
public Guid Id { get; set; }
public OrderStatus Status { get; set; }
public List<OrderLine> Lines { get; set; }
public bool CanBeCancelled() =>
Status == OrderStatus.Pending;
public Result Cancel()
{
if (!CanBeCancelled())
return Result.Failure("Нельзя отменить подтверждённый заказ.");
Status = OrderStatus.Cancelled;
return Result.Success();
}
}
CancelOrder, GetOrder и UpdateOrder все используют одни бизнес-правила. Логика живёт в одном месте. Т.е. разные вертикальные срезы могут использовать одну и ту же модель домена.
Уровень 3: Логика, специфичная для конкретного объекта (сохраняйте её локально)
Логика, общая для связанных срезов, таких как CreateOrder и UpdateOrder, не обязательно должна быть глобальной. Создайте общую папку (из каждого правила есть исключения) внутри функции:
📂 Features
└📂 Orders
├📂 CreateOrder
├📂 UpdateOrder
├📂 GetOrder
└📂 Shared
├📄 OrderValidator.cs
└📄 OrderPricingService.cs
Это также имеет скрытое преимущество. Если вы удалите функцию Orders, общая логика будет удалена вместе с ней. Никакого зомби-кода.
Наконец рассмотрим несколько сложных сценариев, которые большинство упускают из виду.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/vertical-slice-architecture-where-does-the-shared-logic-live
👍10
День 2500. #Architecture
Архитектура Вертикальных Срезов. Где Живёт Общая Логика? Окончание
Начало
Продолжение
Совместное использование между функциями
Что насчёт совместного использования кода между несвязанными функциями в VSA? CreateOrder должен проверять наличие клиента. GenerateInvoice должен рассчитывать налог. И Orders и Customers должны форматировать уведомления. Это не совсем вписывается в папку Shared определённой функции. Куда деть эту логику?
1. Действительно ли нужен общий код?
Большинство случаев «совместного использования» между функциями — это просто замаскированный доступ к данным. Если CreateOrder нужны данные о клиентах, он напрямую обращается к БД. Он не обращается к функции Customers. Каждый срез владеет своим доступом к данным. Сущность Customer является общей (находится в домене), но Orders и Customers не имеют общего сервиса.
2. Когда действительно нужна общая логика
- Логика домена (бизнес-правила, калькуляции) → Domain/Services
- Инфраструктура (внешние API, форматирование) → Infrastructure/Services
CreateOrder и GenerateInvoice могут использовать этот код без привязки друг к другу.
Прежде чем создавать какой-либо кросс-функциональный сервис, спросите себя: может ли эта логика размещаться в доменной сущности? Большая часть «общей бизнес-логики» на самом деле представляет собой доступ к данным, доменную логику, принадлежащую сущности, или преждевременную абстракцию.
Если нужно вызвать побочный эффект в другой функции, лучше использовать сообщения и события. В качестве альтернативы, функция, которую вы хотите вызвать, может использовать фасад (открытый API) для этой операции.
3. Когда дублирование — правильный выбор
Иногда «общий» код только кажется общим:
Они идентичны. Возникает соблазн создать SharedOrderDto. Не поддавайтесь ему.
На следующей неделе в GetOrder понадобится URL трекинга. Но CreateOrder выполняется до отправки, поэтому не имеет URL. Если бы используете общий класс DTO, у вас появится свойство, допускающее NULL, которое в половине случаев остаётся пустым, что сбивает с толку. Дублирование дешевле, чем неправильная абстракция.
Структура Проекта
Вот как может выглядеть структура проекта VSA:
-
-
-
-
-
Правила
1. Функции владеют своими моделями запросов/ответов. Исключений нет.
2. Перемещайте бизнес-логику в домен. Сущности и объекты-значения — лучшее место для совместного использования бизнес-правил.
3. Сохраняйте совместное использование семейств функций локальным. Если это нужно только срезам Order, храните его в Features/Orders/Shared (не стесняйтесь найти более подходящее название, чем Shared).
4. Инфраструктура по умолчанию является общей. Контексты БД, HTTP-клиенты, ведение журнала. Это технические вопросы.
5. Применяйте правило трёх. Не извлекайте данные, пока не найдёте 3 реальных использования идентичной, стабильной логики.
Источник: https://www.milanjovanovic.tech/blog/vertical-slice-architecture-where-does-the-shared-logic-live
Архитектура Вертикальных Срезов. Где Живёт Общая Логика? Окончание
Начало
Продолжение
Совместное использование между функциями
Что насчёт совместного использования кода между несвязанными функциями в VSA? CreateOrder должен проверять наличие клиента. GenerateInvoice должен рассчитывать налог. И Orders и Customers должны форматировать уведомления. Это не совсем вписывается в папку Shared определённой функции. Куда деть эту логику?
1. Действительно ли нужен общий код?
Большинство случаев «совместного использования» между функциями — это просто замаскированный доступ к данным. Если CreateOrder нужны данные о клиентах, он напрямую обращается к БД. Он не обращается к функции Customers. Каждый срез владеет своим доступом к данным. Сущность Customer является общей (находится в домене), но Orders и Customers не имеют общего сервиса.
2. Когда действительно нужна общая логика
- Логика домена (бизнес-правила, калькуляции) → Domain/Services
- Инфраструктура (внешние API, форматирование) → Infrastructure/Services
// Domain/Services/TaxCalculator.cs
public class TaxCalculator
{
public decimal CalculateTax(
Address address, decimal subtotal)
{
var rate = GetTaxRate(address.State, address.Country);
return subtotal * rate;
}
}
CreateOrder и GenerateInvoice могут использовать этот код без привязки друг к другу.
Прежде чем создавать какой-либо кросс-функциональный сервис, спросите себя: может ли эта логика размещаться в доменной сущности? Большая часть «общей бизнес-логики» на самом деле представляет собой доступ к данным, доменную логику, принадлежащую сущности, или преждевременную абстракцию.
Если нужно вызвать побочный эффект в другой функции, лучше использовать сообщения и события. В качестве альтернативы, функция, которую вы хотите вызвать, может использовать фасад (открытый API) для этой операции.
3. Когда дублирование — правильный выбор
Иногда «общий» код только кажется общим:
// Features/Orders/GetOrder
public record GetOrderResponse(Guid Id, decimal Total, string Status);
// Features/Orders/CreateOrder
public record CreateOrderResponse(Guid Id, decimal Total, string Status);
Они идентичны. Возникает соблазн создать SharedOrderDto. Не поддавайтесь ему.
На следующей неделе в GetOrder понадобится URL трекинга. Но CreateOrder выполняется до отправки, поэтому не имеет URL. Если бы используете общий класс DTO, у вас появится свойство, допускающее NULL, которое в половине случаев остаётся пустым, что сбивает с толку. Дублирование дешевле, чем неправильная абстракция.
Структура Проекта
Вот как может выглядеть структура проекта VSA:
📂 src
└📂 Features
│ ├📂 Orders
│ │ ├📂 CreateOrder
│ │ ├📂 UpdateOrder
│ │ └📂 Shared
│ ├📂 Customers
│ │ ├📂 GetCustomer
│ │ └📂 Shared
│ └📂 Invoices
│ └📂 GenerateInvoice
└📂 Domain
│ ├📂 Entities
│ ├📂 ValueObjects
│ └📂 Services
└📂 Infrastructure
│ ├📂 Persistence
│ └📂 Services
└📂 Shared
└📂 Behaviors
-
Features — Изолированные срезы. Каждый со своими моделями запроса/ответа. -
Features/[Name]/Shared — Локальный общий код между связанными срезами.-
Domain — Сущности, объекты-значения и доменные сервисы. Общая бизнес-логика живёт здесь.-
Infrastructure — Технические вопросы.-
Shared — Только общее поведение.Правила
1. Функции владеют своими моделями запросов/ответов. Исключений нет.
2. Перемещайте бизнес-логику в домен. Сущности и объекты-значения — лучшее место для совместного использования бизнес-правил.
3. Сохраняйте совместное использование семейств функций локальным. Если это нужно только срезам Order, храните его в Features/Orders/Shared (не стесняйтесь найти более подходящее название, чем Shared).
4. Инфраструктура по умолчанию является общей. Контексты БД, HTTP-клиенты, ведение журнала. Это технические вопросы.
5. Применяйте правило трёх. Не извлекайте данные, пока не найдёте 3 реальных использования идентичной, стабильной логики.
Источник: https://www.milanjovanovic.tech/blog/vertical-slice-architecture-where-does-the-shared-logic-live
👍17
День 2501.
Сегодня порекомендую видео от моего любимого докладчика Дилана Бити. Недавно на конференции NDC в Копенгагене он выступал с докладом “Algorithms Demystified”.
Бывало ли у вас, что вы застревали на какой-то проблеме с кодом? Возможно, реализовывали какую-то функцию в одном из своих проектов, или решали задачи на LeetCode или Advent of Code, и застряли. Вы не можете понять, как получить нужный результат. Поэтому спрашиваете у коллег, на Stack Overflow или Reddit, и получаете ответ вроде: «Да тут просто надо использовать алгоритм Дейкстры»… и ваш мозг зависает. Использовать что? Вы гуглите и обнаруживаете, что это «поиск кратчайших путей между узлами во взвешенном графе», и теперь нужно узнать, что такое «узел» и что такое «взвешенный граф»… а затем понять, как превратить всё это в работающий код.
Алгоритмы — ключ ко всем видам функций и систем, от сетей до автокоррекции, и понимание того, как они работают, поможет вам создавать более качественное программное обеспечение, исправлять едва заметные ошибки и решать задачи на Advent of Code. В этом докладе Дилан расскажет о некоторых из его любимых алгоритмов, объяснит их важность и поможет вам понять, что они делают и как.
Сегодня порекомендую видео от моего любимого докладчика Дилана Бити. Недавно на конференции NDC в Копенгагене он выступал с докладом “Algorithms Demystified”.
Бывало ли у вас, что вы застревали на какой-то проблеме с кодом? Возможно, реализовывали какую-то функцию в одном из своих проектов, или решали задачи на LeetCode или Advent of Code, и застряли. Вы не можете понять, как получить нужный результат. Поэтому спрашиваете у коллег, на Stack Overflow или Reddit, и получаете ответ вроде: «Да тут просто надо использовать алгоритм Дейкстры»… и ваш мозг зависает. Использовать что? Вы гуглите и обнаруживаете, что это «поиск кратчайших путей между узлами во взвешенном графе», и теперь нужно узнать, что такое «узел» и что такое «взвешенный граф»… а затем понять, как превратить всё это в работающий код.
Алгоритмы — ключ ко всем видам функций и систем, от сетей до автокоррекции, и понимание того, как они работают, поможет вам создавать более качественное программное обеспечение, исправлять едва заметные ошибки и решать задачи на Advent of Code. В этом докладе Дилан расскажет о некоторых из его любимых алгоритмов, объяснит их важность и поможет вам понять, что они делают и как.
YouTube
Algorithms Demystified - Dylan Beattie - NDC Copenhagen 2025
This talk was recorded at NDC Copenhagen in Copenhagen, Denmark. #ndccopenhagen #ndcconferences #developer #softwaredeveloper
Attend the next NDC conference near you:
https://ndcconferences.com
https://ndccopenhagen.com/
Subscribe to our YouTube channel…
Attend the next NDC conference near you:
https://ndcconferences.com
https://ndccopenhagen.com/
Subscribe to our YouTube channel…
51👍13
День 2502. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
11. Различия между современным .NET и .NET Framework
«Объясните ключевые различия между современным .NET и .NET Framework? Также обсудите ситуации, в которых один вариант может быть более подходящим, чем другой».
Хороший ответ
Современный .NET и .NET Framework — это фреймворки, разработанные Microsoft, но они предназначены для разных потребностей и сценариев.
- Поддержка кроссплатформенности: современный .NET кроссплатформенный, поддерживает Windows, Linux и macOS, что делает его подходящим для приложений, требующих широкого охвата в различных операционных системах. .NET Framework же ограничен Windows.
- Производительность и масштабируемость: современный .NET оптимизирован для производительности и масштабируемости. Он включает в себя такие усовершенствования, как веб-сервер Kestrel и компилятор RyuJIT, которые эффективнее и быстрее аналогов из .NET Framework. Это делает современный .NET идеальным для создания высокопроизводительных и масштабируемых веб-приложений.
- Архитектура микросервисов: современный .NET разработан для поддержки архитектуры микросервисов, позволяя разработчикам создавать и развертывать независимо обновляемые и масштабируемые сервисы. Он легковесен и имеет встроенную поддержку технологий контейнеризации, таких как Docker.
- Совместимость: .NET Framework поддерживает более широкий спектр старых API .NET и сторонние библиотеки, которые могут быть недоступны в современной .NET. Этот подход часто выбирают для устаревших приложений, использующих эти API, и для проектов, где совместимость со старыми технологиями .NET критически важна.
Инструменты и обновления: современный .NET выигрывает от параллельного управления версиями, которое позволяет нескольким версиям среды выполнения существовать на одной машине. Это особенно полезно для тестирования новых функций, не затрагивая существующие приложения. С другой стороны, .NET Framework не поддерживает эту функцию и требует обновления единственного экземпляра на хосте.
Подводя итог, можно выделить следующие сценарии, в которых один вариант может быть более подходящим:
- Современный .NET подходит для новых корпоративных приложений, особенно тех, которые требуют кроссплатформенной функциональности, архитектуры микросервисов или требуют работы в контейнерных средах.
- .NET Framework подойдёт для поддержки существующих приложений, использующих старые библиотеки или специфичные для Windows API, которые не поддерживаются современным .NET.
Часто даваемый неудачный ответ
«NET Core — это просто более новая версия .NET Framework, поэтому всегда лучше использовать .NET Core для всех проектов, поскольку он новее и в конечном итоге заменит .NET Framework.»
Этот ответ чрезмерно упрощает различия и игнорирует конкретные сильные стороны и варианты использования каждого фреймворка:
- Непонимание области применения и вариантов использования: Предложение использовать .NET Core или современный .NET для всех проектов игнорирует сценарии, в которых .NET Framework может по-прежнему быть необходим из-за совместимости со старыми технологиями и широкого использования в корпоративном секторе.
- Игнорирование проблем совместимости: Утверждение, что современный .NET заменит .NET Framework, не учитывает тот факт, что некоторые приложения зависят от функций и библиотек, доступных только в .NET Framework.
- Упрощение выбора технологий: Выбор технологий должен основываться на конкретных требованиях, возможностях и стратегических целях, а не просто Выбор чего-то, что новее.
Эта распространённая ошибка происходит из-за отсутствия глубокого понимания экосистем обоих фреймворков, что приводит к неверной оценке их применимости.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
11. Различия между современным .NET и .NET Framework
«Объясните ключевые различия между современным .NET и .NET Framework? Также обсудите ситуации, в которых один вариант может быть более подходящим, чем другой».
Хороший ответ
Современный .NET и .NET Framework — это фреймворки, разработанные Microsoft, но они предназначены для разных потребностей и сценариев.
- Поддержка кроссплатформенности: современный .NET кроссплатформенный, поддерживает Windows, Linux и macOS, что делает его подходящим для приложений, требующих широкого охвата в различных операционных системах. .NET Framework же ограничен Windows.
- Производительность и масштабируемость: современный .NET оптимизирован для производительности и масштабируемости. Он включает в себя такие усовершенствования, как веб-сервер Kestrel и компилятор RyuJIT, которые эффективнее и быстрее аналогов из .NET Framework. Это делает современный .NET идеальным для создания высокопроизводительных и масштабируемых веб-приложений.
- Архитектура микросервисов: современный .NET разработан для поддержки архитектуры микросервисов, позволяя разработчикам создавать и развертывать независимо обновляемые и масштабируемые сервисы. Он легковесен и имеет встроенную поддержку технологий контейнеризации, таких как Docker.
- Совместимость: .NET Framework поддерживает более широкий спектр старых API .NET и сторонние библиотеки, которые могут быть недоступны в современной .NET. Этот подход часто выбирают для устаревших приложений, использующих эти API, и для проектов, где совместимость со старыми технологиями .NET критически важна.
Инструменты и обновления: современный .NET выигрывает от параллельного управления версиями, которое позволяет нескольким версиям среды выполнения существовать на одной машине. Это особенно полезно для тестирования новых функций, не затрагивая существующие приложения. С другой стороны, .NET Framework не поддерживает эту функцию и требует обновления единственного экземпляра на хосте.
Подводя итог, можно выделить следующие сценарии, в которых один вариант может быть более подходящим:
- Современный .NET подходит для новых корпоративных приложений, особенно тех, которые требуют кроссплатформенной функциональности, архитектуры микросервисов или требуют работы в контейнерных средах.
- .NET Framework подойдёт для поддержки существующих приложений, использующих старые библиотеки или специфичные для Windows API, которые не поддерживаются современным .NET.
Часто даваемый неудачный ответ
«NET Core — это просто более новая версия .NET Framework, поэтому всегда лучше использовать .NET Core для всех проектов, поскольку он новее и в конечном итоге заменит .NET Framework.»
Этот ответ чрезмерно упрощает различия и игнорирует конкретные сильные стороны и варианты использования каждого фреймворка:
- Непонимание области применения и вариантов использования: Предложение использовать .NET Core или современный .NET для всех проектов игнорирует сценарии, в которых .NET Framework может по-прежнему быть необходим из-за совместимости со старыми технологиями и широкого использования в корпоративном секторе.
- Игнорирование проблем совместимости: Утверждение, что современный .NET заменит .NET Framework, не учитывает тот факт, что некоторые приложения зависят от функций и библиотек, доступных только в .NET Framework.
- Упрощение выбора технологий: Выбор технологий должен основываться на конкретных требованиях, возможностях и стратегических целях, а не просто Выбор чего-то, что новее.
Эта распространённая ошибка происходит из-за отсутствия глубокого понимания экосистем обоих фреймворков, что приводит к неверной оценке их применимости.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👎12👍6
День 2503. #SystemDesign101
8 Ключевых Концепций DDD
1. Предметно-ориентированное проектирование (Domain Driven Design)
Предполагает разработку программного обеспечения посредством моделирования предметной области. Единый язык — одна из ключевых концепций DDD. Модель предметной области — связующее звено между бизнес-доменом и программным обеспечением.
2. Бизнес-сущности
Использование моделей может помочь в выражении бизнес-концепций и знаний, а также в руководстве дальнейшей разработкой программного обеспечения, такого как базы данных, API и т. д.
3. Ограниченные контексты
Для моделирования бизнес-корреляций используются гибкие границы между наборами предметных моделей.
4. Агрегация
Агрегат — это кластер связанных объектов (сущностей и объектов-значений), которые рассматриваются как единое целое при изменении данных. Обращение ко всем объектам агрегата должно осуществляться только через главный объект – корень агрегата.
5. Сущности или объекты-значения
Помимо корней агрегатов и сущностей, существуют некоторые модели, которые выглядят как одноразовые: у них нет собственного идентификатора, который бы их идентифицировал, но они являются частью некой сущности, представляющей собой набор из нескольких полей.
6. Манипуляции с моделями
В DDD для манипуляций с этими моделями используется ряд объектов, которые действуют как «операторы»:
- фабрики – для создания объектов,
- сервисы – для управления моделями, исполняя бизнес-логику,
- репозитории – для сохранения и извлечения моделей из хранилища.
7. Многоуровневая архитектура
Чтобы лучше организовать различные объекты в проекте, необходимо упростить сложность проектов, разбив их на уровни, подобно компьютерной сети.
8. Построение модели предметной области
Набор методов для извлечения моделей предметной области из бизнес-знаний.
Источник: https://bytebytego.com/guides/8-key-concepts-in-ddd/
8 Ключевых Концепций DDD
1. Предметно-ориентированное проектирование (Domain Driven Design)
Предполагает разработку программного обеспечения посредством моделирования предметной области. Единый язык — одна из ключевых концепций DDD. Модель предметной области — связующее звено между бизнес-доменом и программным обеспечением.
2. Бизнес-сущности
Использование моделей может помочь в выражении бизнес-концепций и знаний, а также в руководстве дальнейшей разработкой программного обеспечения, такого как базы данных, API и т. д.
3. Ограниченные контексты
Для моделирования бизнес-корреляций используются гибкие границы между наборами предметных моделей.
4. Агрегация
Агрегат — это кластер связанных объектов (сущностей и объектов-значений), которые рассматриваются как единое целое при изменении данных. Обращение ко всем объектам агрегата должно осуществляться только через главный объект – корень агрегата.
5. Сущности или объекты-значения
Помимо корней агрегатов и сущностей, существуют некоторые модели, которые выглядят как одноразовые: у них нет собственного идентификатора, который бы их идентифицировал, но они являются частью некой сущности, представляющей собой набор из нескольких полей.
6. Манипуляции с моделями
В DDD для манипуляций с этими моделями используется ряд объектов, которые действуют как «операторы»:
- фабрики – для создания объектов,
- сервисы – для управления моделями, исполняя бизнес-логику,
- репозитории – для сохранения и извлечения моделей из хранилища.
7. Многоуровневая архитектура
Чтобы лучше организовать различные объекты в проекте, необходимо упростить сложность проектов, разбив их на уровни, подобно компьютерной сети.
8. Построение модели предметной области
Набор методов для извлечения моделей предметной области из бизнес-знаний.
Источник: https://bytebytego.com/guides/8-key-concepts-in-ddd/
👎8👍6
День 2504. #ЗаметкиНаПолях
Использование Сортируемых UUID/GUID в Entity Framework
В .NET 9 появились методы Guid.CreateVersion7() и Guid.CreateVersion7(DateTimeOffset) для создания сортируемых UUID/GUID по времени их создания. Это может быть особенно полезно в базах данных, где требуется поддерживать хронологический порядок записей (плюс некоторые преимущества в плане производительности). В настоящее время в Entity Framework нет встроенного способа настроить использование этих методов при генерации новых GUID для первичных ключей. Но мы можем сделать это самостоятельно.
Генератор значений
Для начала нам нужно создать пользовательский генератор значений, который будет использовать Guid.CreateVersion7() для генерации новых GUID:
Использование:
И вуаля! Теперь, когда для объектов MyEntity свойство Id будет генерироваться в виде UUID версии 7. Если вы хотите, чтобы появился более удобный способ генерации таких значений в Entity Framework, можете проголосовать за этот тикет.
Источник: https://steven-giesel.com/blogPost/d6150b89-a3ef-407e-add2-7afa4a2a8729/using-sortable-uuid-guids-in-entity-framework
Использование Сортируемых UUID/GUID в Entity Framework
В .NET 9 появились методы Guid.CreateVersion7() и Guid.CreateVersion7(DateTimeOffset) для создания сортируемых UUID/GUID по времени их создания. Это может быть особенно полезно в базах данных, где требуется поддерживать хронологический порядок записей (плюс некоторые преимущества в плане производительности). В настоящее время в Entity Framework нет встроенного способа настроить использование этих методов при генерации новых GUID для первичных ключей. Но мы можем сделать это самостоятельно.
Генератор значений
Для начала нам нужно создать пользовательский генератор значений, который будет использовать Guid.CreateVersion7() для генерации новых GUID:
public class UUIDv7Generator
: ValueGenerator<Guid>
{
public override bool
GeneratesTemporaryValues => false;
public override Guid Next(EntityEntry entry)
=> Guid.CreateVersion7();
}
Использование:
public class MyDbContext : DbContext
{
protected override void
OnModelCreating(ModelBuilder mb)
{
mb.Entity<MyEntity>()
.Property(e => e.Id)
.HasValueGenerator<UUIDv7Generator>()
.ValueGeneratedOnAdd();
}
}
И вуаля! Теперь, когда для объектов MyEntity свойство Id будет генерироваться в виде UUID версии 7. Если вы хотите, чтобы появился более удобный способ генерации таких значений в Entity Framework, можете проголосовать за этот тикет.
Источник: https://steven-giesel.com/blogPost/d6150b89-a3ef-407e-add2-7afa4a2a8729/using-sortable-uuid-guids-in-entity-framework
👍27
День 2505. #ЗаметкиНаПолях
Кэширование в Redis с Двойным Ключом. Начало
Кэширование в Redis — это просто… пока вы не осознаете, что вашей сущности нужны два разных ключа поиска: один внутренний и один внешний. Тут всё становится сложнее: если не быть внимательным, приложение может стать жертвой устаревших данных, пропущенных инвалидаций и несогласованного состояния между сервисами.
Проблема
Представьте, что вы разрабатываете типичное приложение. У каждого пользователя есть:
- UserId (GUID, внутренний, неизменяемый)
- Email (используется для входа, внешний, изменяемый)
Теперь рассмотрим, как система взаимодействует с этим профилем пользователя.
Вариант 1. Аутентификация (Email → User)
При входе в систему служба идентификации должна:
1. Найти пользователя по email;
2. Загрузить его учётные данные;
3. Загрузить его профиль (роли, настройки и т.п.)
Для этого требуется быстрый поиск по email. Выполнение этого SQL-запроса в часы пиковых нагрузок может замедлить систему. Redis решает эту проблему.
Но остальная бизнес-логика ведёт себя по-другому…
Вариант 2. Внутренние микросервисы (UserId → Профиль)
Биллинг, уведомления, аналитика и журналы аудита — все они идентифицируют пользователей по внутреннему UserId. Поэтому они ожидают быстрого поиска по UserId.
Если вы кэшируете только по одному ключу:
- UserId - трафик аутентификаций приводит к задержкам,
- Email - внутренние сервисы постоянно используют БД.
Кэширование с двойным ключом
Кэширование с двойным ключом позволяет получить доступ к одной и той же сущности, используя два разных ключа:
- По внутреннему, стабильному ключу (UserId),
- По внешнему, доступному пользователю, ключу (Email).
Но есть и более серьёзная проблема…
Наивный разработчик может сказать: «Просто храните полный JSON объекта под обоими ключами!»
Это сработает, на время. Но,
- если пользователь сменит email, нужно удалить запись со старым ключом;
- приходится удалять два элемента каждый раз при изменении пользовательских данных;
- сбои в работе сети между двумя вызовами StringSetAsync = повреждение кэша;
- вы тратите память, храня дублирующиеся JSON-объекты.
Поэтому профессиональные системы используют другой подход.
Единый источник данных + ключ индекса
Храним полный профиль пользователя ОДИН РАЗ:
Храним индекс Email → UserId:
Это обеспечивает:
- отсутствие дублирования JSON-данных,
- отсутствие несоответствий между первичными и вторичными ключами,
- безопасные обновления email,
- простую инвалидацию,
- производительный поиск в обоих случаях.
Далее посмотрим, как это реализовать в .NET.
Окончание следует…
Источник: https://thecodeman.net/posts/dual-key-redis-caching-in-dotnet
Кэширование в Redis с Двойным Ключом. Начало
Кэширование в Redis — это просто… пока вы не осознаете, что вашей сущности нужны два разных ключа поиска: один внутренний и один внешний. Тут всё становится сложнее: если не быть внимательным, приложение может стать жертвой устаревших данных, пропущенных инвалидаций и несогласованного состояния между сервисами.
Проблема
Представьте, что вы разрабатываете типичное приложение. У каждого пользователя есть:
- UserId (GUID, внутренний, неизменяемый)
- Email (используется для входа, внешний, изменяемый)
Теперь рассмотрим, как система взаимодействует с этим профилем пользователя.
Вариант 1. Аутентификация (Email → User)
При входе в систему служба идентификации должна:
1. Найти пользователя по email;
2. Загрузить его учётные данные;
3. Загрузить его профиль (роли, настройки и т.п.)
Для этого требуется быстрый поиск по email. Выполнение этого SQL-запроса в часы пиковых нагрузок может замедлить систему. Redis решает эту проблему.
Но остальная бизнес-логика ведёт себя по-другому…
Вариант 2. Внутренние микросервисы (UserId → Профиль)
Биллинг, уведомления, аналитика и журналы аудита — все они идентифицируют пользователей по внутреннему UserId. Поэтому они ожидают быстрого поиска по UserId.
Если вы кэшируете только по одному ключу:
- UserId - трафик аутентификаций приводит к задержкам,
- Email - внутренние сервисы постоянно используют БД.
Кэширование с двойным ключом
Кэширование с двойным ключом позволяет получить доступ к одной и той же сущности, используя два разных ключа:
- По внутреннему, стабильному ключу (UserId),
- По внешнему, доступному пользователю, ключу (Email).
Но есть и более серьёзная проблема…
Наивный разработчик может сказать: «Просто храните полный JSON объекта под обоими ключами!»
Это сработает, на время. Но,
- если пользователь сменит email, нужно удалить запись со старым ключом;
- приходится удалять два элемента каждый раз при изменении пользовательских данных;
- сбои в работе сети между двумя вызовами StringSetAsync = повреждение кэша;
- вы тратите память, храня дублирующиеся JSON-объекты.
Поэтому профессиональные системы используют другой подход.
Единый источник данных + ключ индекса
Храним полный профиль пользователя ОДИН РАЗ:
user : data : {userId} → JSONХраним индекс Email → UserId:
user : email : {email} → userIdЭто обеспечивает:
- отсутствие дублирования JSON-данных,
- отсутствие несоответствий между первичными и вторичными ключами,
- безопасные обновления email,
- простую инвалидацию,
- производительный поиск в обоих случаях.
Далее посмотрим, как это реализовать в .NET.
Окончание следует…
Источник: https://thecodeman.net/posts/dual-key-redis-caching-in-dotnet
1👍16
День 2506. #ЗаметкиНаПолях
Кэширование в Redis с Двойным Ключом. Окончание
Начало
Реализация Двойного Ключа в .NET
1. DTO + Хелпер для ключей Redis
2. Кэширование пользователя (атомарная запись)
- оба ключа обновляются совместно;
- исключён риск частичных записей;
- JSON хранится 1 раз.
3. Поиск по UserId
4. Поиск по Email
5. Безопасная обработка изменений email
- старый индекс удаляется, добавляется новый;
- ключ данных не изменяется;
- нет дублирования JSON;
- нет несогласованного состояния кэша.
Если вы не будете использовать кэширование с двойным ключом, то рано или поздно, вы столкнетесь с…
- устаревшими данными в кэше;
- неработающим входом в систему (изменён email, но кэш не обновился);
- внутренними микросервисами, возвращающими устаревшие значения;
- «фантомными пользователями» в логах;
- и т.п.
Большинство этих ошибок никогда не проявятся в процессе разработки — только в рабочей среде под реальной нагрузкой.
Шаблон кэширования с двойным ключом универсален для современных систем:
1. Электронная коммерция
- ProductId → данные
- Артикул → ProductId
2. CMS
- ContentId → данные
- Slug → ContentId
3. IoT
- DeviceId → данные
- MAC-адрес/Серийный номер → DeviceId
Итого
Кэширование Redis с двойным ключом — не оптимизация, а фундаментальная архитектура для современных систем .NET, использующих Redis. Его следует использовать, когда:
- сущность имеет несколько идентификаторов;
- один или несколько из этих идентификаторов изменяемы;
- необходим быстрый поиск из разных контекстов;
- нужно избежать дублирования JSON в Redis;
- важна согласованность кэша под нагрузкой.
Правильный шаблон:
- один канонический ключ данных;
- несколько легковесных ключей индекса;
- атомарные обновления для обеспечения согласованности.
Так вы избежите почти всех проблем с несогласованностью кэша еще до их появления.
Источник: https://thecodeman.net/posts/dual-key-redis-caching-in-dotnet
Кэширование в Redis с Двойным Ключом. Окончание
Начало
Реализация Двойного Ключа в .NET
1. DTO + Хелпер для ключей Redis
public class UserDto
{
public Guid UserId { get; set; }
public string Email { get; set; }
public string DisplayName { get; set; }
public string TimeZone { get; set; }
}
public static class UserKeys
{
public static string Data(Guid id)
=> $"user:data:{id}";
public static string Email(string email)
=> $"user:email:{email.ToLowerInvariant()}";
}
2. Кэширование пользователя (атомарная запись)
public async Task CacheUserAsync(UserDto user)
{
var dataKey = UserKeys.Data(user.UserId);
var emailKey = UserKeys.Email(user.Email);
var json = JsonSerializer.Serialize(user);
var tran = _db.CreateTransaction();
tran.StringSetAsync(dataKey, json, TimeSpan.FromMinutes(10));
tran.StringSetAsync(emailKey, user.UserId.ToString(), TimeSpan.FromMinutes(10));
await tran.ExecuteAsync();
}
- оба ключа обновляются совместно;
- исключён риск частичных записей;
- JSON хранится 1 раз.
3. Поиск по UserId
public async Task<UserDto?>
GetByIdAsync(Guid userId)
{
var json = await _db.StringGetAsync(
UserKeys.Data(userId));
return json.IsNullOrEmpty
? null
: JsonSerializer.Deserialize<UserDto>(json);
}
4. Поиск по Email
public async Task<UserDto?>
GetByEmailAsync(string email)
{
var id = await _db.StringGetAsync(UserKeys.Email(email));
if (string.IsNullOrEmpty(id)) return null;
var userId = Guid.Parse(id);
return await GetByIdAsync(userId);
}
5. Безопасная обработка изменений email
public async Task UpdateEmailAsync(
Guid userId,
string oldEmail,
string newEmail)
{
var oldKey = UserKeys.Email(oldEmail);
var newKey = UserKeys.Email(newEmail);
var tran = _db.CreateTransaction();
// ... обновляем данные пользователя
tran.KeyDeleteAsync(oldKey);
tran.StringSetAsync(newKey, userId.ToString());
await tran.ExecuteAsync();
}
- старый индекс удаляется, добавляется новый;
- ключ данных не изменяется;
- нет дублирования JSON;
- нет несогласованного состояния кэша.
Если вы не будете использовать кэширование с двойным ключом, то рано или поздно, вы столкнетесь с…
- устаревшими данными в кэше;
- неработающим входом в систему (изменён email, но кэш не обновился);
- внутренними микросервисами, возвращающими устаревшие значения;
- «фантомными пользователями» в логах;
- и т.п.
Большинство этих ошибок никогда не проявятся в процессе разработки — только в рабочей среде под реальной нагрузкой.
Шаблон кэширования с двойным ключом универсален для современных систем:
1. Электронная коммерция
- ProductId → данные
- Артикул → ProductId
2. CMS
- ContentId → данные
- Slug → ContentId
3. IoT
- DeviceId → данные
- MAC-адрес/Серийный номер → DeviceId
Итого
Кэширование Redis с двойным ключом — не оптимизация, а фундаментальная архитектура для современных систем .NET, использующих Redis. Его следует использовать, когда:
- сущность имеет несколько идентификаторов;
- один или несколько из этих идентификаторов изменяемы;
- необходим быстрый поиск из разных контекстов;
- нужно избежать дублирования JSON в Redis;
- важна согласованность кэша под нагрузкой.
Правильный шаблон:
- один канонический ключ данных;
- несколько легковесных ключей индекса;
- атомарные обновления для обеспечения согласованности.
Так вы избежите почти всех проблем с несогласованностью кэша еще до их появления.
Источник: https://thecodeman.net/posts/dual-key-redis-caching-in-dotnet
👍8
День 2507. #ЧтоНовенького #NET11
Помощник по MIME-типам в .NET 11
Как обычно, сразу за выходом новой версии .NET, сразу начинаем обсуждать потенциальные новинки следующей версии. .NET 11 обещают выход новых хелпер-методов: MediaTypeMap.GetMediaType и MediaTypeMap.GetExtension, которые значительно упростят веб-разработку!
MediaTypeMap.GetMediaType
Основная идея заключается в получении MIME-типов из указанного файла или расширения:
Мы также можем использовать «обратный поиск» с помощью MediaTypeMap.GetExtension:
Заметьте, что известные расширения начинаются с точки.
См. также:
Обсуждение на GitHub: https://github.com/dotnet/runtime/issues/121017
Полный пул-реквест: https://github.com/dotnet/runtime/pull/121776
Источник: https://steven-giesel.com/blogPost/8f41d316-f6ac-4712-bf11-ea36b7c2f2e6/mime-type-helper-in-net-11
Помощник по MIME-типам в .NET 11
Как обычно, сразу за выходом новой версии .NET, сразу начинаем обсуждать потенциальные новинки следующей версии. .NET 11 обещают выход новых хелпер-методов: MediaTypeMap.GetMediaType и MediaTypeMap.GetExtension, которые значительно упростят веб-разработку!
MediaTypeMap.GetMediaType
Основная идея заключается в получении MIME-типов из указанного файла или расширения:
using System.Net.Mime;
// text/css
_ = MediaTypeMap.GetMediaType("myfile.css");
// application/json
_ = MediaTypeMap.GetMediaType("resource.json");
_ = MediaTypeMap.GetMediaType("rEsOuRCe.JsOn");
// null, т.к. smith – неизвестное расширение
_ = MediaTypeMap.GetMediaType("jon.smith");
// Он также работает с расширениями или с целыми путями
// text/css
_ = MediaTypeMap.GetMediaType("/home/user/myfile.css");
_ = MediaTypeMap.GetMediaType("css");
Мы также можем использовать «обратный поиск» с помощью MediaTypeMap.GetExtension:
using System.Net.Mime;
// ".pdf"
_ = MediaTypeMap.GetExtension("application/pdf");
// ".jpg"
_ = MediaTypeMap.GetExtension("image/jpeg");
// null для неизвестных
_ = MediaTypeMap.GetExtension("madeup/mimetype");
_ = MediaTypeMap.GetExtension("/some/path/basic.css");
Заметьте, что известные расширения начинаются с точки.
См. также:
Обсуждение на GitHub: https://github.com/dotnet/runtime/issues/121017
Полный пул-реквест: https://github.com/dotnet/runtime/pull/121776
Источник: https://steven-giesel.com/blogPost/8f41d316-f6ac-4712-bf11-ea36b7c2f2e6/mime-type-helper-in-net-11
4👍2