День 2471. #ЗаметкиНаПолях
Вы Всё Ещё Используете new Random()? Окончание
Начало
Проблема с Random в .NET Framework
Основных проблем с Random в .NET Framework две:
1. При вызове new Random() в .NET Framework он использует системные часы для посева;
2. Системные часы имеют конечное разрешение.
Т.е. при быстром последовательном вызове new Random() в .NET Framework могут возникнуть два экземпляра Random с одинаковым начальным значением, что означает, что экземпляры Random вернут идентичную последовательность чисел:
При выполнении этого кода несколько чисел (а то и все) могут быть одинаковыми, а это не то, чего мы хотим.
Решение
Создать один экземпляр Random, который используется исключительно для предоставления начальных значений для остальных экземпляров Random. Поскольку начальные значения случайны, экземпляры Random не коррелируют, и вы можете полностью избежать описанной выше проблемы. Этот подход к инициализации используется по умолчанию в .NET Core, поэтому проблема возникает только в .NET Framework.
Мы можем реализовать тот же подход в классе-обёртке ThreadSafeRandom, показанном ниже. Этот класс использует [ThreadStatic], избегая проблем с инициализацией в .NET Framework:
Эту потокобезопасную реализацию, можно использовать во всех версиях фреймворка:
Если вы используете .NET6+, рекомендуется использовать встроенный Random.Shared. В более ранних версиях вы можете использовать приведённый выше ThreadSafeRandom для решения этих проблем. Если вы ориентируетесь как на .NET6+, так и на .NET Framework, можно использовать директивы #if для разделения реализации в зависимости от целевой среды.
Источник: https://andrewlock.net/building-a-thread-safe-random-implementation-for-dotnet-framework/
Вы Всё Ещё Используете new Random()? Окончание
Начало
Проблема с Random в .NET Framework
Основных проблем с Random в .NET Framework две:
1. При вызове new Random() в .NET Framework он использует системные часы для посева;
2. Системные часы имеют конечное разрешение.
Т.е. при быстром последовательном вызове new Random() в .NET Framework могут возникнуть два экземпляра Random с одинаковым начальным значением, что означает, что экземпляры Random вернут идентичную последовательность чисел:
// <TargetFramework>net48</TargetFramework>
Parallel.For(0, 10, x =>
{
Random rnd = new();
var value = rnd.Next();
Console.WriteLine(value);
});
При выполнении этого кода несколько чисел (а то и все) могут быть одинаковыми, а это не то, чего мы хотим.
Решение
Создать один экземпляр Random, который используется исключительно для предоставления начальных значений для остальных экземпляров Random. Поскольку начальные значения случайны, экземпляры Random не коррелируют, и вы можете полностью избежать описанной выше проблемы. Этот подход к инициализации используется по умолчанию в .NET Core, поэтому проблема возникает только в .NET Framework.
Мы можем реализовать тот же подход в классе-обёртке ThreadSafeRandom, показанном ниже. Этот класс использует [ThreadStatic], избегая проблем с инициализацией в .NET Framework:
internal static class ThreadSafeRandom
{
[ThreadStatic]
private static Random? _local;
private static readonly
Random Global = new Random();
private static Random Instance
{
get
{
if (_local is null)
{
int seed;
// избегаем конкурентного доступа
lock (Global)
{
seed = Global.Next();
}
_local = new Random(seed);
}
return _local;
}
}
public static int Next() => Instance.Next();
}
Эту потокобезопасную реализацию, можно использовать во всех версиях фреймворка:
Parallel.For(0, 10, x =>
{
var value = ThreadSafeRandom.Next();
Console.WriteLine(value);
});
Если вы используете .NET6+, рекомендуется использовать встроенный Random.Shared. В более ранних версиях вы можете использовать приведённый выше ThreadSafeRandom для решения этих проблем. Если вы ориентируетесь как на .NET6+, так и на .NET Framework, можно использовать директивы #if для разделения реализации в зависимости от целевой среды.
Источник: https://andrewlock.net/building-a-thread-safe-random-implementation-for-dotnet-framework/
👍11
День 2472. #ЗаметкиНаПолях
AsNoTrackingWithIdentityResolution в EF
При работе с Entity Framework Core понимание поведения отслеживания изменений критически важно как для производительности, так и для согласованности данных. Про AsNoTracking() знают все, а вот его мощная альтернатива AsNoTrackingWithIdentityResolution() не так популярна.
Что такое AsNoTracking()?
По умолчанию EF Core отслеживает все сущности, возвращаемые запросами в трекере изменений. Отслеживание обеспечивает:
- автоматическое обнаружение изменений в сущностях;
- операции обновления без явного присоединения сущностей;
- разрешение идентификаторов (гарантируя наличие только одного экземпляра каждой сущности в памяти).
Однако отслеживание сопряжено с накладными расходами. При выполнении операций только чтения, когда не требуется обновлять данные, AsNoTracking() повышает производительность, полностью отменяя отслеживание изменений:
Проблема - дублирование экземпляров сущностей. Если трекер изменений не будет выполнять разрешение идентификаторов, в памяти может оказаться несколько экземпляров одной и той же сущности, например:
Если несколько заказов принадлежат одному и тому же клиенту, вы получите отдельные экземпляры Customer для каждого заказа, даже если они представляют одну и ту же запись в БД. Это означает:
- увеличение потребления памяти,
- возможность путаницы при сравнении сущностей,
- потерю ссылочной целостности в графе объектов.
AsNoTrackingWithIdentityResolution()
AsNoTrackingWithIdentityResolution() решает эту проблему, объединяя преимущества двух подходов:
- отсутствие отслеживания изменений (выигрыш в производительности),
- разрешение идентификаторов (гарантия единственности экземпляров сущностей).
При этом EF Core:
- выполняет запрос без присоединения сущностей к трекеру изменений;
- сохраняет временную карту идентификации во время материализации запроса;
- гарантирует, что сущности с одинаковым ключом ссылаются на один и тот же экземпляр;
- удаляет карту идентификации после завершения запроса.
Т.е. что вы получаете разрешение идентификации во время выполнения запроса без дополнительных затрат на отслеживание изменений.
Также можно установить AsNoTrackingWithIdentityResolution() как поведение трекера по умолчанию:
Когда использовать
1. Отслеживание по умолчанию:
- необходимо обновлять, удалять или отслеживать изменения сущностей;
- небольшие наборы результатов, где затраты на отслеживание незначительны;
- необходимо автоматическое обнаружение изменений.
2. AsNoTracking:
- операции только чтения;
- нет свойств навигации или связанных сущностей;
- максимальная производительность критически важна;
- результаты не содержат дублирующихся сущностей.
3. AsNoTrackingWithIdentityResolution:
- операции только чтения с include/join;
- требуется ссылочная целостность в графе объектов;
- запросы, возвращающие одну сущность несколько раз;
- требуется сравнивать сущности по ссылке.
Источник: https://bartwullems.blogspot.com/2025/10/understanding-asnotrackingwithidentityr.html
AsNoTrackingWithIdentityResolution в EF
При работе с Entity Framework Core понимание поведения отслеживания изменений критически важно как для производительности, так и для согласованности данных. Про AsNoTracking() знают все, а вот его мощная альтернатива AsNoTrackingWithIdentityResolution() не так популярна.
Что такое AsNoTracking()?
По умолчанию EF Core отслеживает все сущности, возвращаемые запросами в трекере изменений. Отслеживание обеспечивает:
- автоматическое обнаружение изменений в сущностях;
- операции обновления без явного присоединения сущностей;
- разрешение идентификаторов (гарантируя наличие только одного экземпляра каждой сущности в памяти).
Однако отслеживание сопряжено с накладными расходами. При выполнении операций только чтения, когда не требуется обновлять данные, AsNoTracking() повышает производительность, полностью отменяя отслеживание изменений:
var customers = await context.Customers
.AsNoTracking()
.ToListAsync();
Проблема - дублирование экземпляров сущностей. Если трекер изменений не будет выполнять разрешение идентификаторов, в памяти может оказаться несколько экземпляров одной и той же сущности, например:
var orders = await context.Orders
.AsNoTracking()
.Include(o => o.Customer)
.Where(o => o.OrderDate > DateTime.Now.AddDays(-30))
.ToListAsync();
Если несколько заказов принадлежат одному и тому же клиенту, вы получите отдельные экземпляры Customer для каждого заказа, даже если они представляют одну и ту же запись в БД. Это означает:
- увеличение потребления памяти,
- возможность путаницы при сравнении сущностей,
- потерю ссылочной целостности в графе объектов.
AsNoTrackingWithIdentityResolution()
AsNoTrackingWithIdentityResolution() решает эту проблему, объединяя преимущества двух подходов:
- отсутствие отслеживания изменений (выигрыш в производительности),
- разрешение идентификаторов (гарантия единственности экземпляров сущностей).
var orders = await context.Orders
.AsNoTrackingWithIdentityResolution()
.Include(o => o.Customer)
.Where(o => o.OrderDate > DateTime.Now.AddDays(-30))
.ToListAsync();
// Теперь все заказы от одного клиента
// будут ссылаться на один экземпляр Customer в памяти
При этом EF Core:
- выполняет запрос без присоединения сущностей к трекеру изменений;
- сохраняет временную карту идентификации во время материализации запроса;
- гарантирует, что сущности с одинаковым ключом ссылаются на один и тот же экземпляр;
- удаляет карту идентификации после завершения запроса.
Т.е. что вы получаете разрешение идентификации во время выполнения запроса без дополнительных затрат на отслеживание изменений.
Также можно установить AsNoTrackingWithIdentityResolution() как поведение трекера по умолчанию:
protected override void OnConfiguring(
DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(connectionString)
.UseQueryTrackingBehavior(
QueryTrackingBehavior.NoTrackingWithIdentityResolution);
}
Когда использовать
1. Отслеживание по умолчанию:
- необходимо обновлять, удалять или отслеживать изменения сущностей;
- небольшие наборы результатов, где затраты на отслеживание незначительны;
- необходимо автоматическое обнаружение изменений.
2. AsNoTracking:
- операции только чтения;
- нет свойств навигации или связанных сущностей;
- максимальная производительность критически важна;
- результаты не содержат дублирующихся сущностей.
3. AsNoTrackingWithIdentityResolution:
- операции только чтения с include/join;
- требуется ссылочная целостность в графе объектов;
- запросы, возвращающие одну сущность несколько раз;
- требуется сравнивать сущности по ссылке.
Источник: https://bartwullems.blogspot.com/2025/10/understanding-asnotrackingwithidentityr.html
👍42
День 2473. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы). Я решил разобрать их тут. Начнём с 4го, поскольку первые 3 приведены в его книге, обзор на которую я недавно выкладывал.
7. Делегаты и события
«Можете ли вы объяснить разницу между делегатами и событиями в C# и как они используются для реализации паттерна Наблюдатель? Приведите пример сценария, в котором вы бы их использовали».
Хороший ответ
«В C# делегаты — это объекты, ссылающиеся на методы. Это типобезопасные указатели на функции, позволяющие передавать методы в качестве параметров. Делегаты используются для определения методов обратного вызова и реализации обработки событий в .NET. Событие, с другой стороны, — это механизм, который использует делегаты для предоставления уведомлений о важных событиях в системе. События, по сути, являются обёртками для делегатов, добавляющими уровень защиты; они могут быть вызваны только из класса, в котором они объявлены, что предотвращает прямую активацию события внешними объектами.
Паттерн Наблюдатель реализуется с помощью делегатов и событий, позволяя подписчикам регистрироваться у поставщика событий. При существенном изменении условия или состояния генерируется событие, и подписанные наблюдатели получают уведомления.
Например, в UI-приложении можно использовать событие для уведомления компонентов UI об изменении модели данных. Можно определить делегат, определяющий сигнатуру методов, которые могут реагировать на событие, и событие, основанное на этом делегате. Компоненты UI, которым необходимо обновляться при изменении модели, могут подписаться на это событие и реагировать соответствующим образом.
Этот подход отделяет отправителя события от получателей, поскольку отправителю не нужно знать ничего о методах, которые будут обрабатывать событие. Он обеспечивает эффективное взаимодействие компонентов без тесной связи.»
Часто встречающийся некорректный ответ
«Делегаты и события в C# — это одно и то же; они используются для указания методов, которые можно вызвать позже. Я использую их взаимозаменяемо в своих приложениях для запуска методов из других классов».
Этот ответ демонстрирует путаницу между делегатами и событиями и упускает из виду ключевые аспекты их использования:
- Путаница между делегатами и событиями: В этом ответе не проводится должного различия между делегатами и событиями. Хотя оба метода действительно используют указатели на методы, события — это особый вид применения делегатов, предназначенный для обеспечения инкапсуляции и предоставления стандартного шаблона обработки событий. События защищают список вызовов делегата, гарантируя, что только класс-владелец может вызвать событие.
- Неправильное использование делегатов и событий: Заявление об их взаимозаменяемости упускает из виду цель событий — инкапсуляцию экземпляра делегата и безопасное управление списками подписок. Такое неправильное использование может привести к неудачному выбору архитектуры, когда делегаты могут быть доступны публично, что может поставить под угрозу целостность и безопасность приложения.
- Недостаточное понимание инкапсуляции и слабой связанности: правильное использование событий критически важно для поддержания слабой связанности между компонентами приложения, что является ключевым преимуществом, которое не учитывается в этом ответе.
Эта ошибка обычно возникает из-за поверхностного понимания модели делегатов и событий в C# и указывает на необходимость более глубокого изучения основополагающих концепций .NET в области событийно-управляемого программирования.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы). Я решил разобрать их тут. Начнём с 4го, поскольку первые 3 приведены в его книге, обзор на которую я недавно выкладывал.
7. Делегаты и события
«Можете ли вы объяснить разницу между делегатами и событиями в C# и как они используются для реализации паттерна Наблюдатель? Приведите пример сценария, в котором вы бы их использовали».
Хороший ответ
«В C# делегаты — это объекты, ссылающиеся на методы. Это типобезопасные указатели на функции, позволяющие передавать методы в качестве параметров. Делегаты используются для определения методов обратного вызова и реализации обработки событий в .NET. Событие, с другой стороны, — это механизм, который использует делегаты для предоставления уведомлений о важных событиях в системе. События, по сути, являются обёртками для делегатов, добавляющими уровень защиты; они могут быть вызваны только из класса, в котором они объявлены, что предотвращает прямую активацию события внешними объектами.
Паттерн Наблюдатель реализуется с помощью делегатов и событий, позволяя подписчикам регистрироваться у поставщика событий. При существенном изменении условия или состояния генерируется событие, и подписанные наблюдатели получают уведомления.
Например, в UI-приложении можно использовать событие для уведомления компонентов UI об изменении модели данных. Можно определить делегат, определяющий сигнатуру методов, которые могут реагировать на событие, и событие, основанное на этом делегате. Компоненты UI, которым необходимо обновляться при изменении модели, могут подписаться на это событие и реагировать соответствующим образом.
Этот подход отделяет отправителя события от получателей, поскольку отправителю не нужно знать ничего о методах, которые будут обрабатывать событие. Он обеспечивает эффективное взаимодействие компонентов без тесной связи.»
Часто встречающийся некорректный ответ
«Делегаты и события в C# — это одно и то же; они используются для указания методов, которые можно вызвать позже. Я использую их взаимозаменяемо в своих приложениях для запуска методов из других классов».
Этот ответ демонстрирует путаницу между делегатами и событиями и упускает из виду ключевые аспекты их использования:
- Путаница между делегатами и событиями: В этом ответе не проводится должного различия между делегатами и событиями. Хотя оба метода действительно используют указатели на методы, события — это особый вид применения делегатов, предназначенный для обеспечения инкапсуляции и предоставления стандартного шаблона обработки событий. События защищают список вызовов делегата, гарантируя, что только класс-владелец может вызвать событие.
- Неправильное использование делегатов и событий: Заявление об их взаимозаменяемости упускает из виду цель событий — инкапсуляцию экземпляра делегата и безопасное управление списками подписок. Такое неправильное использование может привести к неудачному выбору архитектуры, когда делегаты могут быть доступны публично, что может поставить под угрозу целостность и безопасность приложения.
- Недостаточное понимание инкапсуляции и слабой связанности: правильное использование событий критически важно для поддержания слабой связанности между компонентами приложения, что является ключевым преимуществом, которое не учитывается в этом ответе.
Эта ошибка обычно возникает из-за поверхностного понимания модели делегатов и событий в C# и указывает на необходимость более глубокого изучения основополагающих концепций .NET в области событийно-управляемого программирования.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
1👍23
День 2474. #ЗаметкиНаПолях #Git
Использование Условных Включений Git для Нескольких Конфигураций
При работе с несколькими репозиториями Git часто требуются разные настройки для каждого контекста. Например, вы можете использовать личную электронную почту для пет-проектов и рабочую — для корпоративных. Хотя Git можно настроить глобально или для каждого репозитория, ручное управление этими настройками становится утомительным и подверженным ошибкам.
Функция условных включений Git решает эту проблему, автоматически применяя различные конфигурации на основе таких критериев, как расположение репозитория, удалённый URL или имя ветки. Это гарантирует, что правильные настройки всегда будут использоваться без ручного вмешательства.
Вот практический пример применения определённой конфигурации при работе с вашими личными репозиториями GitHub:
Когда Git обнаруживает удалённый URL, соответствующий этому шаблону, он автоматически добавляет настройки из файла .gitconfig-github-personal, который может содержать ваш личный email и ключ подписи.
Git поддерживает несколько типов условий для условных включений, каждый из которых предназначен для разных случаев использования.
1.
Условие gitdir сопоставляет репозитории на основе пути к их файловой системе. В Unix-подобных системах оно чувствительно к регистру.
Это применит .gitconfig-personal ко всем репозиториям в ~/personal/ и .gitconfig-work – к репозиториям в ~/work/.
2.
Условие onbranch применяет конфигурацию на основе ветки, для которой сделан checkout:
Это полезно для применения различных настроек при работе с ветками production и development.
3.
Условие hasconfig проверяет существование определённого значения конфигурации и его соответствие шаблону. Это особенно полезно для сопоставления удалённых URL:
Это позволяет применять различные конфигурации в зависимости от того, где размещён ваш репозиторий, независимо от его локального пути.
Источник: https://www.meziantou.net/using-git-conditional-includes-for-multiple-configurations.htm
Использование Условных Включений Git для Нескольких Конфигураций
При работе с несколькими репозиториями Git часто требуются разные настройки для каждого контекста. Например, вы можете использовать личную электронную почту для пет-проектов и рабочую — для корпоративных. Хотя Git можно настроить глобально или для каждого репозитория, ручное управление этими настройками становится утомительным и подверженным ошибкам.
Функция условных включений Git решает эту проблему, автоматически применяя различные конфигурации на основе таких критериев, как расположение репозитория, удалённый URL или имя ветки. Это гарантирует, что правильные настройки всегда будут использоваться без ручного вмешательства.
Вот практический пример применения определённой конфигурации при работе с вашими личными репозиториями GitHub:
[includeIf "hasconfig:remote.*.url:https://github.com/myname/*"]
path = .gitconfig-github-personal
Когда Git обнаруживает удалённый URL, соответствующий этому шаблону, он автоматически добавляет настройки из файла .gitconfig-github-personal, который может содержать ваш личный email и ключ подписи.
Git поддерживает несколько типов условий для условных включений, каждый из которых предназначен для разных случаев использования.
1.
gitdir — Сопоставление по пути к репозиториюУсловие gitdir сопоставляет репозитории на основе пути к их файловой системе. В Unix-подобных системах оно чувствительно к регистру.
[includeIf "gitdir:~/personal/"]
path = .gitconfig-personal
[includeIf "gitdir:~/work/"]
path = .gitconfig-work
Это применит .gitconfig-personal ко всем репозиториям в ~/personal/ и .gitconfig-work – к репозиториям в ~/work/.
gitdir/i аналогичен для нечувствительного к регистру сопоставления.2.
onbranch – Сопоставление по текущей веткеУсловие onbranch применяет конфигурацию на основе ветки, для которой сделан checkout:
[includeIf "onbranch:main"]
path = .gitconfig-production
[includeIf "onbranch:feature/**"]
path = .gitconfig-development
Это полезно для применения различных настроек при работе с ветками production и development.
3.
hasconfig — Сопоставление с существующей конфигурациейУсловие hasconfig проверяет существование определённого значения конфигурации и его соответствие шаблону. Это особенно полезно для сопоставления удалённых URL:
[includeIf "hasconfig:remote.*.url:https://github.com/myname/*"]
path = .gitconfig-github-personal
[includeIf "hasconfig:remote.*.url:[email protected]:*/**"]
path = .gitconfig-company
Это позволяет применять различные конфигурации в зависимости от того, где размещён ваш репозиторий, независимо от его локального пути.
Источник: https://www.meziantou.net/using-git-conditional-includes-for-multiple-configurations.htm
👍23
День 2475. #SystemDesign101
Типы Серверов в Современных Системах
Источник: https://blog.bytebytego.com
Типы Серверов в Современных Системах
Источник: https://blog.bytebytego.com
👍15👎1
День 2476. #ЗаметкиНаПолях
Больше Никаких Моков ILogger'a
Имитация зависимостей в процессе разработки может быть сложной задачей, особенно если вы не являетесь владельцем интерфейса, а взаимодействие с интерфейсом в коде осуществляется через методы расширения. Так происходит с ILogger из базового пакета Microsoft. Интерфейс ILogger относительно прост, однако вы редко взаимодействуете с ним напрямую. Как же проверить, что был вызван правильный метод и параметры, чтобы гарантировать корректность ведения журнала?
Можно использовать мок ILogger<> с помощью Moq и внедрить его в тестируемую систему, а затем проверять, вызывается ли он. Однако это может быть затруднительно, и также потребуется копировать код проверки мока. Это неидеально.
FakeLogger предоставляет полный доступ ко всем данным, которые вызываются при использовании ILogger в коде, и позволяет проверять их значения в тестах.
Начало работы
С момента создания он немного изменился. Сейчас его можно найти в пакете Microsoft.Extensions.Diagnostics.Testing. После добавления NuGet-пакета, его можно использовать в модульных тестах. Рассмотрим простой сервис в качестве тестируемой системы:
В сервисе выше мы хотим проверить, что при выполнении метода LogMe вызывается соответствующий метод логирования.
Начнём с создания экземпляра тестируемой системы, а для этого необходимо создать экземпляр FakeLogger:
Т.к. FakeLogger<> реализует нужный интерфейс, мы можем использовать его для создания нашего DemoService.
После выполнения блока Act https://t.iss.one/NetDeveloperDiary/1003 теста мы можем проверить результаты:
Самый простой способ сделать это — посмотреть свойство LatestRecord. Это выведет последний вызов экземпляра логгера в тестируемой системе, и это всё, что нужно для базового использования!
Итого
FakeLogger удобно использовать при тестировании системы, требующей реализации ILogger. Он позволяет тестировать корректные уровни логирования, сообщения и структурированные данные, указанные в системных сообщениях логгера.
Источник: https://adamstorr.co.uk/blog/no-more-mocking-ilogger/
Больше Никаких Моков ILogger'a
Имитация зависимостей в процессе разработки может быть сложной задачей, особенно если вы не являетесь владельцем интерфейса, а взаимодействие с интерфейсом в коде осуществляется через методы расширения. Так происходит с ILogger из базового пакета Microsoft. Интерфейс ILogger относительно прост, однако вы редко взаимодействуете с ним напрямую. Как же проверить, что был вызван правильный метод и параметры, чтобы гарантировать корректность ведения журнала?
Можно использовать мок ILogger<> с помощью Moq и внедрить его в тестируемую систему, а затем проверять, вызывается ли он. Однако это может быть затруднительно, и также потребуется копировать код проверки мока. Это неидеально.
FakeLogger предоставляет полный доступ ко всем данным, которые вызываются при использовании ILogger в коде, и позволяет проверять их значения в тестах.
Начало работы
С момента создания он немного изменился. Сейчас его можно найти в пакете Microsoft.Extensions.Diagnostics.Testing. После добавления NuGet-пакета, его можно использовать в модульных тестах. Рассмотрим простой сервис в качестве тестируемой системы:
public class DemoService(ILogger<DemoService> logger)
{
public void LogMe()
{
logger.LogInformation("Пишем в лог.");
}
}
В сервисе выше мы хотим проверить, что при выполнении метода LogMe вызывается соответствующий метод логирования.
Начнём с создания экземпляра тестируемой системы, а для этого необходимо создать экземпляр FakeLogger:
var fakeLogger = new FakeLogger<DemoService>();
var sut = new DemoService(fakeLogger);
Т.к. FakeLogger<> реализует нужный интерфейс, мы можем использовать его для создания нашего DemoService.
После выполнения блока Act https://t.iss.one/NetDeveloperDiary/1003 теста мы можем проверить результаты:
fakeLogger
.LatestRecord
.Message
.Should()
.Be("Пишем в лог.");
Самый простой способ сделать это — посмотреть свойство LatestRecord. Это выведет последний вызов экземпляра логгера в тестируемой системе, и это всё, что нужно для базового использования!
Итого
FakeLogger удобно использовать при тестировании системы, требующей реализации ILogger. Он позволяет тестировать корректные уровни логирования, сообщения и структурированные данные, указанные в системных сообщениях логгера.
Источник: https://adamstorr.co.uk/blog/no-more-mocking-ilogger/
👍36
День 2477. #ЗаметкиНаПолях
Тестирование с Использованием FakeLogger
Вместо того, чтобы заниматься сложными настройками моков или проверять вызовы вручную, FakeLogger собирает сообщения журнала, области действия и структурированные данные в памяти, делая утверждения простыми и выразительными. Сегодня рассмотрим, как тестировать области логирования с помощью FakeLogger, зачем нужны области и как использовать их в коде.
Зачем использовать области действия?
Область логирования в .NET позволяет прикреплять контекстную информацию ко всем записям журнала в блоке кода. Представьте себе облегчённый набор пар «ключ-значение», который дополняет сообщения журнала, не заставляя вас предоставлять одни и те же поля в каждую запись журнала.
Например, вы можете добавить идентификатор запроса, идентификатор корреляции или идентификатор сущности в область действия, тем самым предоставляя в каждую запись журнала в этом блоке одинаковые контекстные метаданные. Это значительно упрощает фильтрацию и анализ журналов в распределённых системах или при отладке конкретных запросов.
Использование FakeLogger с областями действия
Начнём с простого метода, использующего область действия журналирования:
Здесь вызов BeginScope создаёт контекст логирования с единственным ключом (id). При вызове lgr.LogInformation область действия автоматически включает эти контекстные данные. Но как это проверить в тесте?
FakeLogger отслеживает каждую запись журнала, включая шаблоны сообщений, уровни ведения журнала, исключения и области действия. Проверим, правильно ли DemoService пишет в лог, включая id:
В этом тесте мы:
- Создаём экземпляр тестируемой системы (SUT), используя FakeLogger<DemoService>.
- Вызываем метод LogMe(), генерирующий одну запись журнала.
- Проверяем содержимое сообщения и проверяем, записана ли коллекция из области действия.
Каждая область действия регистрируется как объект, и в данном случае это Dictionary<string, object>, содержащий id. Этот подход понятен, выразителен и поддерживается фреймворком, устраняя необходимость в пользовательских оболочках для логирования или ненадёжных настройках верификации моков.
Недостаток в том, что вам, вероятно, потребуется знать, как использовалась область действия в коде, чтобы тесты проверяли то, что вам нужно, однако в данном контексте это приемлемо.
Источник: https://adamstorr.co.uk/blog/testing-with-fakelogger-scopes/
Тестирование с Использованием FakeLogger
Вместо того, чтобы заниматься сложными настройками моков или проверять вызовы вручную, FakeLogger собирает сообщения журнала, области действия и структурированные данные в памяти, делая утверждения простыми и выразительными. Сегодня рассмотрим, как тестировать области логирования с помощью FakeLogger, зачем нужны области и как использовать их в коде.
Зачем использовать области действия?
Область логирования в .NET позволяет прикреплять контекстную информацию ко всем записям журнала в блоке кода. Представьте себе облегчённый набор пар «ключ-значение», который дополняет сообщения журнала, не заставляя вас предоставлять одни и те же поля в каждую запись журнала.
Например, вы можете добавить идентификатор запроса, идентификатор корреляции или идентификатор сущности в область действия, тем самым предоставляя в каждую запись журнала в этом блоке одинаковые контекстные метаданные. Это значительно упрощает фильтрацию и анализ журналов в распределённых системах или при отладке конкретных запросов.
Использование FakeLogger с областями действия
Начнём с простого метода, использующего область действия журналирования:
public partial class
DemoService(ILogger<DemoService> lgr)
{
public void LogMe(Guid id)
{
using var _ =
lgr.BeginScope(
new Dictionary<string, object> { { "id", id } });
lgr.LogInformation("Пишем в лог.");
}
}
Здесь вызов BeginScope создаёт контекст логирования с единственным ключом (id). При вызове lgr.LogInformation область действия автоматически включает эти контекстные данные. Но как это проверить в тесте?
FakeLogger отслеживает каждую запись журнала, включая шаблоны сообщений, уровни ведения журнала, исключения и области действия. Проверим, правильно ли DemoService пишет в лог, включая id:
public class DemoServiceTests
{
[Fact]
public void ScopeUsage()
{
var fakeLogger = new FakeLogger<DemoService>();
var sut = new DemoService(fakeLogger);
var id = Guid.NewGuid();
sut.LogMe(id);
fakeLogger
.LatestRecord
.Message
.Should()
.Be("Пишем в лог.");
var scope = fakeLogger
.LatestRecord
.Scopes[0] as Dictionary<string, object>;
scope.Should().NotBeNull();
scope.Keys.Should().Contain("id");
scope.Values.Should().Contain(id);
}
}
В этом тесте мы:
- Создаём экземпляр тестируемой системы (SUT), используя FakeLogger<DemoService>.
- Вызываем метод LogMe(), генерирующий одну запись журнала.
- Проверяем содержимое сообщения и проверяем, записана ли коллекция из области действия.
Каждая область действия регистрируется как объект, и в данном случае это Dictionary<string, object>, содержащий id. Этот подход понятен, выразителен и поддерживается фреймворком, устраняя необходимость в пользовательских оболочках для логирования или ненадёжных настройках верификации моков.
Недостаток в том, что вам, вероятно, потребуется знать, как использовалась область действия в коде, чтобы тесты проверяли то, что вам нужно, однако в данном контексте это приемлемо.
Источник: https://adamstorr.co.uk/blog/testing-with-fakelogger-scopes/
👍7
День 2478. #ЗаметкиНаПолях
Ограничение Доступа к Методу Действия в ASP.NET Core MVC. Начало
Рассмотрим несколько вариантов ограничения доступа к определённому методу действия (или ко всем методам) в контроллере ASP.NET Core MVC.
Мы всегда должны добавлять поддержку аутентификации (кто пользователь) и авторизации (что он может делать) в конвейер ASP.NET Core. Авторизация требует аутентификации, но аутентификация может существовать и сама по себе в определённых схемах аутентификации:
Использование фильтров
Фильтры ASP.NET Core хорошо известны и популярны, и, пожалуй, это самый простой способ ограничить доступ к конечной точке. Фильтры можно применять:
- через атрибут, к методу действия или контроллеру,
- глобально для всех контроллеров и действий.
Пользовательские фильтры
Использование:
Возврат результата осуществляется через свойство Result объекта AuthorizationFilterContext:
-
-
-
-
Продолжение следует…
Источник: https://developmentwithadot.blogspot.com/2025/10/restricting-access-to-action-method-in.html
Ограничение Доступа к Методу Действия в ASP.NET Core MVC. Начало
Рассмотрим несколько вариантов ограничения доступа к определённому методу действия (или ко всем методам) в контроллере ASP.NET Core MVC.
Мы всегда должны добавлять поддержку аутентификации (кто пользователь) и авторизации (что он может делать) в конвейер ASP.NET Core. Авторизация требует аутентификации, но аутентификация может существовать и сама по себе в определённых схемах аутентификации:
builder.Services.AddAuthentication()
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);
builder.Services.AddAuthorization();
Использование фильтров
Фильтры ASP.NET Core хорошо известны и популярны, и, пожалуй, это самый простой способ ограничить доступ к конечной точке. Фильтры можно применять:
- через атрибут, к методу действия или контроллеру,
- глобально для всех контроллеров и действий.
Пользовательские фильтры
IAuthorizationFilter (IAsyncAuthorizationFilter) — интерфейс, определяющий правила авторизации для данной конечной точки. Его можно реализовать с помощью атрибута, который затем применить к методу действия, классу контроллера или глобально ко всем запросам:[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class DayOfWeekFilterAttribute(
params DayOfWeek[] daysOfWeek)
: Attribute, IAsyncAuthorizationFilter
{
public Task OnAuthorizationAsync(
AuthorizationFilterContext context)
{
if (!(daysOfWeek ?? [])
.Contains(DateTime.Today.DayOfWeek))
context.Result = new ForbidResult();
return Task.CompletedTask;
}
}
Использование:
[DaysOfWeek(DayOfWeek.Saturday, DayOfWeek.Sunday)]
public IActionResult Index() { … }
Возврат результата осуществляется через свойство Result объекта AuthorizationFilterContext:
-
ForbidResult: HTTP 403 Forbidden;-
RedirectResult, RedirectToActionResult, RedirectToPageResult, RedirectToRouteResult, LocalRedirectResult: различные виды перенаправлений (HTTP 3xx);-
EmptyResult: ничего с кодом 200 OK;-
StatusCodeResult: пользовательский код статуса.Продолжение следует…
Источник: https://developmentwithadot.blogspot.com/2025/10/restricting-access-to-action-method-in.html
👍15
День 2479. #ЗаметкиНаПолях
Ограничение Доступа к Методу Действия в ASP.NET Core MVC. Продолжение
Начало
Атрибут Authorize
Существует также встроенный атрибут
- Политика: ограничение по именованной политике, которая должна быть определена.
- Роли: одна или несколько ролей, разделённых запятыми. Текущий пользователь должен иметь одну из указанных ролей. Роли берутся из утверждений запроса.
Возможно наличие нескольких атрибутов [Authorize]:
- если в одном [Authorize] несколько ролей, пользователю необходимо иметь одну из них;
- если в нескольких [Authorize] заданы роли, пользователь должен иметь их все.
То же относится и к политикам.
Атрибут
Если мы хотим использовать именованные политики, мы должны определить их:
Теперь можно использовать политику "AdminPolicy":
Роли и политики
Это разные способы управления доступом. Роли напрямую сопоставляются с требованиями аутентификации или группами пользователей, в зависимости от используемого типа аутентификации. Политики же позволяют настраивать требования, которые могут включать роли, но не ограничиваются этим. В определении политики мы можем требовать:
- аутентификации:
- утверждений (claim):
- одной из ролей:
- определённого имени:
- определённого условия:
А также:
- Объединять несколько политик:
- Добавлять требования:
Имена политик должны быть уникальными и могут использоваться в различных местах, связанных с авторизацией.
Также возможно иметь более структурированный и переиспользуемый контроль доступа, об этом далее.
Окончание следует…
Источник: https://developmentwithadot.blogspot.com/2025/10/restricting-access-to-action-method-in.html
Ограничение Доступа к Методу Действия в ASP.NET Core MVC. Продолжение
Начало
Атрибут Authorize
Существует также встроенный атрибут
[Authorize], который может быть применён к классам контроллеров или методам действий, но не реализует ни один из этих интерфейсов. Он допускает несколько ограничений:- Политика: ограничение по именованной политике, которая должна быть определена.
- Роли: одна или несколько ролей, разделённых запятыми. Текущий пользователь должен иметь одну из указанных ролей. Роли берутся из утверждений запроса.
Возможно наличие нескольких атрибутов [Authorize]:
- если в одном [Authorize] несколько ролей, пользователю необходимо иметь одну из них;
- если в нескольких [Authorize] заданы роли, пользователь должен иметь их все.
То же относится и к политикам.
Атрибут
[AllowAnonymous], если присутствует, обходит любой атрибут [Authorize].[Authorize(Roles = "Admin")]
//все методы действия контроллера требуют роли "Admin"
public class AdminController
: Controller
{
public IActionResult Index()
{ … } //требует роли "Admin"
[AllowAnonymous]
public IActionResult SignOut()
{ … } //может быть вызван кем угодно
}
Если мы хотим использовать именованные политики, мы должны определить их:
builder.Services.AddAuthorizationBuilder()
.AddPolicy("AdminPolicy", p =>
{
p.RequireRole("Admin");
p.RequireAuthenticatedUser();
});
Теперь можно использовать политику "AdminPolicy":
[Authorize(Policy = "AdminPolicy")]
public IActionResult Restricted()
{ … } //требует удовлетворять политике "AdminPolicy"
Роли и политики
Это разные способы управления доступом. Роли напрямую сопоставляются с требованиями аутентификации или группами пользователей, в зависимости от используемого типа аутентификации. Политики же позволяют настраивать требования, которые могут включать роли, но не ограничиваются этим. В определении политики мы можем требовать:
- аутентификации:
RequireAuthenticatedUser();- утверждений (claim):
RequireClaim();- одной из ролей:
RequireRole();- определённого имени:
RequireUserName();- определённого условия:
RequireAssertion().А также:
- Объединять несколько политик:
Combine();- Добавлять требования:
AddRequirements().Имена политик должны быть уникальными и могут использоваться в различных местах, связанных с авторизацией.
Также возможно иметь более структурированный и переиспользуемый контроль доступа, об этом далее.
Окончание следует…
Источник: https://developmentwithadot.blogspot.com/2025/10/restricting-access-to-action-method-in.html
👍4
❗️Ваш C# код снова ломается при каждом изменении?
Новая фича — и пол проекта надо править?
Тесты разваливаются при каждом рефакторинге?
Если вы регулярно сталкиваетесь с этим — скорее всего, у вас нет чёткой архитектурной основы.
🔥 На 6-недельном курсе по DDD и архитектуре микросервисов на C# вы:
✔️ Построите бизнес-логику через Aggregates и Domain Events
✔️ Спроектируете микросервис по принципам Clean Architecture
✔️ Напишете читаемые и стабильные модульные и интеграционные тесты
✔️ Научитесь вносить изменения без риска «сломать всё»
✔️ Подключите gRPC, Kafka, CQRS, HTTP и обеспечите Eventual Consistency
✔️ Получите в портфолио продуманный микросервис — шаблон для работы и резюме
👨🏫 Курс ведёт Кирилл Ветчинкин — действующий архитектор в Авито, ex-Staff Engineer в Купер, работает с 2019 года.
📚 В курсе:
- 12 практических модулей
- Реальный кейс — сервис диспетчеризации заказов
- Работа с gRPC, Kafka, CQS, HTTP, Eventual Consistency
- Проверка домашних заданий и разборы кода
- Живые вебинары со спикером
- Чат с экспертом и разбор ваших кейсов
Регистрируйтесь на курс, старт 17 ноября https://microarch.ru/courses/ddd/languages/csharp?utm_source=posev&utm_medium=erid:2VtzquiTbDy&utm_campaign=1
Реклама. ИП Ветчинкин К.Е. ИНН: 773376451099 Erid: 2VtzquiTbDy
Новая фича — и пол проекта надо править?
Тесты разваливаются при каждом рефакторинге?
Если вы регулярно сталкиваетесь с этим — скорее всего, у вас нет чёткой архитектурной основы.
🔥 На 6-недельном курсе по DDD и архитектуре микросервисов на C# вы:
✔️ Построите бизнес-логику через Aggregates и Domain Events
✔️ Спроектируете микросервис по принципам Clean Architecture
✔️ Напишете читаемые и стабильные модульные и интеграционные тесты
✔️ Научитесь вносить изменения без риска «сломать всё»
✔️ Подключите gRPC, Kafka, CQRS, HTTP и обеспечите Eventual Consistency
✔️ Получите в портфолио продуманный микросервис — шаблон для работы и резюме
👨🏫 Курс ведёт Кирилл Ветчинкин — действующий архитектор в Авито, ex-Staff Engineer в Купер, работает с 2019 года.
📚 В курсе:
- 12 практических модулей
- Реальный кейс — сервис диспетчеризации заказов
- Работа с gRPC, Kafka, CQS, HTTP, Eventual Consistency
- Проверка домашних заданий и разборы кода
- Живые вебинары со спикером
- Чат с экспертом и разбор ваших кейсов
Регистрируйтесь на курс, старт 17 ноября https://microarch.ru/courses/ddd/languages/csharp?utm_source=posev&utm_medium=erid:2VtzquiTbDy&utm_campaign=1
Реклама. ИП Ветчинкин К.Е. ИНН: 773376451099 Erid: 2VtzquiTbDy
👎2
День 2480. #ЗаметкиНаПолях
Ограничение Доступа к Методу Действия в ASP.NET Core MVC. Окончание
Начало
Продолжение
Использование обработчиков авторизации
Обработчик авторизации — это реализация IAuthorizationHandler, в виде абстрактного класса AuthorizationHandler<TRequirement>, который принимает требование в качестве параметра. Внутри его метода HandleRequirementAsync мы можем реализовать любую логику, передавая требование в качестве параметра:
Маркерный интерфейс IAuthorizationRequirement не определяет методов. Мы можем добавить свойства в нашу реализацию требования, если хотим передавать данные обработчику.
Подключаем требование к обработчику:
Простой пример выше, принимает в качестве параметра только день недели, но вариантов ограничений бесконечное множество:
- по IP,
- по наличию/отсутствию cookie,
- по заголовкам запроса…
Внутри HandleRequirementAsync запрос либо завершается неудачей (Fail), либо успехом (Succeed). Если зарегистрировано много обработчиков, все они должны успешно завершиться для получения разрешения на доступ к конечной точке. Для всех обработчиков вызывается один и тот же экземпляр параметра AuthorizationHandlerContext, и оттуда можно проверить:
- текущего аутентифицированного пользователя (User),
- контекст (Resource),
- текущий статус запроса (HasFailed, HasSucceeded),
- обработанные требования (Requirements)
- ожидающие обработки требования (PendingRequirements),
- причины отказа (FailureReasons).
Обработчики необходимо зарегистрировать в DI. ASP.NET Core находит соответствующий обработчик, проверяя переданное требование:
Мы должны добавить требования в именованную политику:
Теперь мы можем добавить атрибут [Authorize], который ссылается на новую политику:
Сравнение альтернатив
С [Authorize] мы можем использовать только политики или роли. Определение политики обеспечивает гораздо большую гибкость, поскольку мы можем указать именно то, что нужно, и это можно изменить в любое время. Тем не менее, обработчики авторизации — более отказоустойчивое и универсальное решение, позволяющее инкапсулировать несколько условий с параметрами, а также использовать DI.
Источник: https://developmentwithadot.blogspot.com/2025/10/restricting-access-to-action-method-in.html
Ограничение Доступа к Методу Действия в ASP.NET Core MVC. Окончание
Начало
Продолжение
Использование обработчиков авторизации
Обработчик авторизации — это реализация IAuthorizationHandler, в виде абстрактного класса AuthorizationHandler<TRequirement>, который принимает требование в качестве параметра. Внутри его метода HandleRequirementAsync мы можем реализовать любую логику, передавая требование в качестве параметра:
public record DayOfWeekRequirement(DayOfWeek Day)
: IAuthorizationRequirement
{
}
Маркерный интерфейс IAuthorizationRequirement не определяет методов. Мы можем добавить свойства в нашу реализацию требования, если хотим передавать данные обработчику.
Подключаем требование к обработчику:
public class DayOfWeekAuthHandler
: AuthorizationHandler<DayOfWeekRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext ctx,
DayOfWeekRequirement req)
{
if (DateTime.Today.DayOfWeek == req.Day)
ctx.Succeed(req);
else
ctx.Fail(new AuthorizationFailureReason(this, "Неправильный день недели"));
return Task.CompletedTask;
}
}
Простой пример выше, принимает в качестве параметра только день недели, но вариантов ограничений бесконечное множество:
- по IP,
- по наличию/отсутствию cookie,
- по заголовкам запроса…
Внутри HandleRequirementAsync запрос либо завершается неудачей (Fail), либо успехом (Succeed). Если зарегистрировано много обработчиков, все они должны успешно завершиться для получения разрешения на доступ к конечной точке. Для всех обработчиков вызывается один и тот же экземпляр параметра AuthorizationHandlerContext, и оттуда можно проверить:
- текущего аутентифицированного пользователя (User),
- контекст (Resource),
- текущий статус запроса (HasFailed, HasSucceeded),
- обработанные требования (Requirements)
- ожидающие обработки требования (PendingRequirements),
- причины отказа (FailureReasons).
Обработчики необходимо зарегистрировать в DI. ASP.NET Core находит соответствующий обработчик, проверяя переданное требование:
builder.Services
.AddSingleton<IAuthorizationHandler, DayOfWeekAuthHandler>();
Мы должны добавить требования в именованную политику:
builder.Services.AddAuthorizationBuilder()
.AddPolicy("DayOfWeekPolicy", p =>
{
p.Requirements.Add(new DayOfWeekRequirement(DayOfWeek.Saturday));
p.Requirements.Add(new DayOfWeekRequirement(DayOfWeek.Sunday));
});
Теперь мы можем добавить атрибут [Authorize], который ссылается на новую политику:
[Authorize(Policy = "DayOfWeekPolicy")]
public IActionResult Index() { … }
Сравнение альтернатив
С [Authorize] мы можем использовать только политики или роли. Определение политики обеспечивает гораздо большую гибкость, поскольку мы можем указать именно то, что нужно, и это можно изменить в любое время. Тем не менее, обработчики авторизации — более отказоустойчивое и универсальное решение, позволяющее инкапсулировать несколько условий с параметрами, а также использовать DI.
Источник: https://developmentwithadot.blogspot.com/2025/10/restricting-access-to-action-method-in.html
👍6
День 2481. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
8. Language Integrated Query (LINQ)
«Расскажите, что такое LINQ и как его можно использовать в приложениях .NET? Приведите примеры различных типов поставщиков LINQ и объясните сценарий, в котором использование LINQ повышает читаемость и эффективность кода».
Хороший ответ
«LINQ, или Language Integrated Query, — это набор технологий, основанных на интеграции возможностей запросов непосредственно в язык C#. LINQ представляет шаблоны для извлечения и обновления данных из различных источников с использованием общего синтаксиса. Он абстрагирует базовый источник данных, позволяя разработчикам использовать один и тот же подход к запросам к БД, XML-документам и коллекциям в памяти.
В .NET существует несколько поставщиков LINQ, каждый из которых предназначен для доступа к различным типам данных:
- LINQ to Objects — используется для запросов к коллекциям в памяти, таким как списки или массивы, реализующие интерфейс IEnumerable.
- LINQ to SQL — предназначен для запросов к базам данных SQL непосредственно из C#.
- LINQ to XML (ранее известный как XLINQ) — предоставляет простые в использовании возможности для запросов и работы с XML-документами.
- LINQ to Entities — часть Entity Framework Core, которая позволяет выполнять запросы к реляционным БД и облачным хранилищам данных через ORM EF Core.
Пример использования LINQ для повышения читабельности кода и его эффективность можно оценить в задачах преобразования данных. Например, представим сценарий, в котором нужно отфильтровать и упорядочить список сотрудников по их зарплате. Без LINQ пришлось бы написать несколько строк императивного кода, включая циклы и условные операторы. С LINQ это можно сделать краткого и ясно в одну строчку:
```csharp
var highSalaryEmployees = employees
.Where(e => e.Salary > 150000)
.OrderBy(e => e.Salary);
```
Это не только сокращает объём кода, но и улучшает его читаемость, чётко выражая его назначение.»
Часто встречающийся неправильный ответ
«LINQ — это способ написания SQL-запросов на C#. Он используется, когда нужно выполнять запросы к базам данных, и работает практически так же, как SQL.»
Этот ответ демонстрирует ограниченное понимание LINQ и упускает из виду его более широкие области применения и преимущества:
- Непонимание области применения LINQ
LINQ предоставляет унифицированную модель для запросов к данным в памяти (например, массивам или спискам), XML и даже к наборам данных из API или других внешних сервисов данных, выходящую далеко за рамки простого взаимодействия с БД.
- Упрощение возможностей
Сравнение с SQL упускает из виду интегрированную природу LINQ в C# и его способность беспрепятственно работать с объектно-ориентированным программированием, предлагая такие функции, как отложенное выполнение и строгая типизация, которые выходят за рамки традиционных возможностей SQL.
Эта ошибка обычно возникает, когда разработчик в первую очередь использовал LINQ только в контексте баз данных и может не осознавать или не ценить весь спектр возможностей .NET для различных типов источников данных.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
8. Language Integrated Query (LINQ)
«Расскажите, что такое LINQ и как его можно использовать в приложениях .NET? Приведите примеры различных типов поставщиков LINQ и объясните сценарий, в котором использование LINQ повышает читаемость и эффективность кода».
Хороший ответ
«LINQ, или Language Integrated Query, — это набор технологий, основанных на интеграции возможностей запросов непосредственно в язык C#. LINQ представляет шаблоны для извлечения и обновления данных из различных источников с использованием общего синтаксиса. Он абстрагирует базовый источник данных, позволяя разработчикам использовать один и тот же подход к запросам к БД, XML-документам и коллекциям в памяти.
В .NET существует несколько поставщиков LINQ, каждый из которых предназначен для доступа к различным типам данных:
- LINQ to Objects — используется для запросов к коллекциям в памяти, таким как списки или массивы, реализующие интерфейс IEnumerable.
- LINQ to SQL — предназначен для запросов к базам данных SQL непосредственно из C#.
- LINQ to XML (ранее известный как XLINQ) — предоставляет простые в использовании возможности для запросов и работы с XML-документами.
- LINQ to Entities — часть Entity Framework Core, которая позволяет выполнять запросы к реляционным БД и облачным хранилищам данных через ORM EF Core.
Пример использования LINQ для повышения читабельности кода и его эффективность можно оценить в задачах преобразования данных. Например, представим сценарий, в котором нужно отфильтровать и упорядочить список сотрудников по их зарплате. Без LINQ пришлось бы написать несколько строк императивного кода, включая циклы и условные операторы. С LINQ это можно сделать краткого и ясно в одну строчку:
```csharp
var highSalaryEmployees = employees
.Where(e => e.Salary > 150000)
.OrderBy(e => e.Salary);
```
Это не только сокращает объём кода, но и улучшает его читаемость, чётко выражая его назначение.»
Часто встречающийся неправильный ответ
«LINQ — это способ написания SQL-запросов на C#. Он используется, когда нужно выполнять запросы к базам данных, и работает практически так же, как SQL.»
Этот ответ демонстрирует ограниченное понимание LINQ и упускает из виду его более широкие области применения и преимущества:
- Непонимание области применения LINQ
LINQ предоставляет унифицированную модель для запросов к данным в памяти (например, массивам или спискам), XML и даже к наборам данных из API или других внешних сервисов данных, выходящую далеко за рамки простого взаимодействия с БД.
- Упрощение возможностей
Сравнение с SQL упускает из виду интегрированную природу LINQ в C# и его способность беспрепятственно работать с объектно-ориентированным программированием, предлагая такие функции, как отложенное выполнение и строгая типизация, которые выходят за рамки традиционных возможностей SQL.
Эта ошибка обычно возникает, когда разработчик в первую очередь использовал LINQ только в контексте баз данных и может не осознавать или не ценить весь спектр возможностей .NET для различных типов источников данных.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👍11👎1