.NET Разработчик
6.56K subscribers
443 photos
3 videos
14 files
2.13K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 2453. #МоиИнструменты
RazorConsole
Если среди нас есть те, кто скучает по временам Norton Commander, терпеть не может мышь и кому «все эти ваши UI как кость в горле, дайте консоль», у меня для вас хорошие новости. NuGet-пакет RazorConsole стирает грань между разработкой современных веб-UI и консольными приложениями. Он позволяет создавать сложные интерфейсы в консоли с использованием компонентов Razor.

Стандартный пример в шаблоне приложения Blazor – компонент счётчика. Посмотрим, как это выглядит в RazorConsole.

Для начала создадим новый консольный проект. Добавим в него NuGet-пакет RazorConsole:
dotnet add package RazorConsole.Core

Кроме того, RazorConsole требуется SDK Microsoft.NET.Sdk.Razor для компиляции компонентов Razor. Поэтому нужно обновить файл проекта (.csproj) для его использования:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<!-- … -->
</Project>


Добавим простой компонент Razor в файл Counter.razor:
@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Web
@using RazorConsole.Components

<Rows>
<Columns>
<p>Current count</p>
<Markup Content="@count.ToString()"
Foreground="@Spectre.Console.Color.Green" />
</Columns>

<Columns>
<TextButton Content="+1"
OnClick="Increment"
BackgroundColor="@Spectre.Console.Color.Grey"
FocusedColor="@Spectre.Console.Color.Blue" />

<TextButton Content="-1"
OnClick="Decrement"
BackgroundColor="@Spectre.Console.Color.Grey"
FocusedColor="@Spectre.Console.Color.Blue" />
</Columns>
</Rows>

@code {
private int count = 0;
private void Increment() => count++;
private void Decrement() => count--;
}

Мы разместили текст, элемент счётчика и 2 кнопки (увеличивающую и уменьшающую счётчик). Всё это мы поместили в простую таблицу, используя готовые компоненты Rows и Columns (о них позже).

В файле Program.cs нам осталось добавить всего одну строку:
await AppHost.RunAsync<Counter>();

Пример работы показан на видео ниже. Активная кнопка выделяется голубым, перемещаться можно клавишей Tab, а нажимать на кнопку клавишей Enter.

Таким образом можно создавать UI в консоли, используя знакомые компоненты Razor с полной поддержкой привязки данных, обработки событий и методов жизненного цикла компонентов. Пакет содержит 15 готовых компонентов, охватывающих все необходимые функции:
- Разметка: Grid, Columns, Rows, Align, Padder;
- Поля ввода: TextInput, TextButton, Select;
- Отображение: Markup, Panel, Border, Figlet, SyntaxHighlighter, Table;
- Утилиты: Spinner, Newline.
Также имеется интерактивная галерея компонентов, которая поставляется как dotnet утилита RazorConsole.Gallery. Она содержит документацию по всем компонентам.

Источник: https://github.com/LittleLittleCloud/RazorConsole/
This media is not supported in your browser
VIEW IN TELEGRAM
👍12
День 2454. #ЧтоНовенького
Вышел .NET 10 Release Candidate 2
Microsoft выпустили .NET 10 Release Candidate 2, финальную предварительную сборку перед релизом. Как сообщает команда .NET, RC 2 поставляется с лицензией на поддержку go-live, что позволяет разворачивать продуктовую версию и одновременно проверять платформу перед её официальным релизом. Сборка поддерживается в Visual Studio 2026 Insiders и Visual Studio Code с помощью C# Dev Kit.

Официальная дата выпуска .NET 10 — 11 ноября 2025 года. Это будет релиз с долгосрочной поддержкой (LTS), обеспечивающий три года исправлений и обновлений. Release Candidate 1 (RC1) был доступен 9 сентября, а RC2 — 14 октября. В Microsoft заявили, что этот релиз в первую очередь ориентирован на валидацию, качество и стабильность, а не на добавление новых функций. Команда сделала акцент на постепенном улучшении качества, чтобы обеспечить плавный переход к GA и совместимость со всеми поддерживаемыми рабочими нагрузками.

1. MAUI
Windows теперь поддерживает разрешения на доступ к микрофону через Permissions.RequestAsync<Permissions.Microphone>(), обеспечивая единую модель разрешений для всех платформ. Android получает поддержку SafeAreaEdges, что улучшает поведение макета при отрисовке от края до края и наложениях клавиатуры.

Также улучшили генерацию исходного кода XAML, предлагающую более быструю отладку генерации представлений и упрощённый механизм настройки через свойство <MauiXamlInflator>SourceGen</MauiXamlInflator>. В Microsoft описали эти обновления как часть продолжающейся работы по повышению производительности и предсказуемости разработки MAUI.

Для Android, в RC 2 представлены привязки API 36.1, разработанные совместно с командой платформы Uno. Проекты могут быть ориентированы на net10.0-android36.1 для доступа к новейшим API платформы, при этом EnablePreviewFeatures по-прежнему временно требуется. В этом выпуске также продолжается экспериментальное внедрение CoreCLR для Android, позволяя разработчикам отключать Mono (UseMonoRuntime=false) и запускать приложения в новой среде выполнения. Хотя эта функция пока не готова к использованию в промышленной среде, по заявлению Microsoft, она представляет собой важный шаг к унификации среды выполнения на разных платформах.

Для разработчиков Apple теперь доступны привязки Xcode 26 для .NET для iOS, macOS, Mac Catalyst и tvOS, что обеспечивает совместимость с новейшими SDK Apple и единообразие между целевыми платформами .NET 9 и .NET 10.

2. Entity Framework Core
Добавлены обновления стабильности и надёжности, такие как улучшенная обработка сложных сопоставлений JSON, уточнённые границы транзакций миграции, поддержка повторных запросов через ExecutionStrategy и новые предупреждения анализатора о небезопасной конкатенации SQL.

3. SDK
Добавлена возможность запускать задачи MSBuild на базе dotnet в Visual Studio и msbuild.exe, устраняя давний разрыв между средами сборки dotnet и .NET Framework. Объявляя задачи с Runtime="NET" и TaskFactory="TaskHostFactory", авторы могут повторно использовать одну и ту же реализацию в CLI и IDE без необходимости настройки на несколько платформ.

Как пояснила команда разработчиков, эта функция знаменует собой первый шаг в более масштабной модернизации MSBuild. В будущих выпусках планируется добавить дополнительные возможности в MSBuild, чтобы упростить написание и использование задач .NET, включая:
- Автоматическое обнаружение и загрузку задач .NET без необходимости указания метаданных среды выполнения или TaskFactory;
- Снижение нагрузки на производительность IPC между движком MSBuild и задачами при выполнении вне процесса;
- Поддержку функции Host Object для задач .NET, выполняемых вне процесса.

.NET 10 RC 2 предназначена для проверки качества релиза, что открывает путь к полноценному релизу в следующем месяце. Разработчикам рекомендуется протестировать приложения с RC 2 и поделиться отзывами в официальном обсуждении на GitHub до релиза .NET 10 — 11 ноября 2025 года.

Источник: https://www.infoq.com/news/2025/10/dotnet-10-rc-2-release/
👍2
День 2455. #ЗаметкиНаПолях
Когда Type.FullName Возвращает null
Немного бесполезной информации вам в ленту. Казалось бы, имя типа в .NET всегда должно быть известно. Однако сигнатура метода Type.FullName такая:
public abstract string? FullName { get; }


Такое поведение может показаться неожиданным, но существуют определённые сценарии, в которых среда выполнения .NET не может сгенерировать корректное полное имя для типа. Вот два основных случая, когда Type.FullName возвращает null.

1. Обобщённые типы с открытыми параметрами типа
При создании обобщённого типа, содержащего несвязанные обобщённые параметры:
var list = typeof(IList<>);
var dict = typeof(IDictionary<,>);
var listOfDictionaries = list.MakeGenericType(dict);
// IList<IDictionary<,>>
Assert.Null(listOfDictionaries.FullName);


2. Указатели на функции
Указатели на функции, введённые в C#9, также имеют null в FullName:
var functionPointerType = typeof(delegate*<int, void>);
Assert.Null(functionPointerType.FullName);


Источник: https://www.meziantou.net/understanding-when-type-fullname-returns-null-in-dotnet.htm
👍14
День 2456. #TipsAndTricks
6 Шагов для Правильной Настройки Нового .NET-проекта. Начало

Начинать новый .NET-проект всегда волнительно. Но легко пропустить подготовительную работу, которая делает проект масштабируемым и поддерживаемым. Вот несколько ключевых шагов, которые значительно облегчат вам (и вашим коллегам) жизнь в дальнейшем.

1. Единый стиль кода
Файл .editorconfig гарантирует, что все участники команды будут использовать одинаковые соглашения по форматированию и именованию, что позволяет избежать несоответствий в отступах и случайных правил именования.
Можно создать его прямо в Visual Studio. Щелкните правой кнопкой на решении Add -> New Editorconfig (Добавить -> Новый Editorconfig). Конфигурация по умолчанию — отличное начало. Но вы можете настроить её дополнительно в соответствии с предпочтениями команды. Разместите файл в корне решения, чтобы все проекты следовали одним и тем же правилам. При необходимости можно переопределить определённые настройки во вложенных папках, поместив туда свой файл .editorconfig. Вот пара примеров:
- из репозитория среды исполнения .NET
- от Милана общий для проектов .NET

2. Централизованная конфигурации сборки
Файл Directory.Build.props позволяет определить параметры сборки, применяемые к каждому проекту в решении:
<Project>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>

Это позволяет сохранить чистоту и единообразие ваших файлов .csproj, поскольку нет необходимости повторять эти свойства в каждом проекте. Если позже вы захотите включить статические анализаторы или настроить параметры сборки, вы можете сделать это один раз здесь. Преимущество в том, что файлы .csproj становятся практически пустыми, большую часть времени содержащими только ссылки на NuGet-пакеты.

3. Централизованное управление пакетами
По мере роста решения управление версиями NuGet-пакетов в нескольких проектах становится проблематичным. Именно здесь на помощь приходит централизованное управление пакетами. Создайте файл с именем Directory.Packages.props в корне:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>

<ItemGroup>
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
<PackageVersion Include="SonarAnalyzer.CSharp" Version="10.15.0.120848" />
</ItemGroup>
</Project>

Теперь, когда нужно сослаться на NuGet-пакет в проекте, не нужно указывать версию. Можно использовать только имя пакета:
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />

Всё управление версиями осуществляется централизованно. Это упрощает обновление зависимостей и позволяет избежать дрейфа версий между проектами. При необходимости вы по-прежнему можете переопределять версии в отдельных проектах.

Окончание следует…

Источник:
https://www.milanjovanovic.tech/blog/6-steps-for-setting-up-a-new-dotnet-project-the-right-way
1👍38
День 2457. #TipsAndTricks
6 Шагов для Правильной Настройки Нового .NET-проекта. Окончание

Начало

4. Статический анализ кода
Помогает выявлять потенциальные ошибки и поддерживать качество кода. В .NET есть набор встроенных анализаторов, но есть отличный NuGet-пакет SonarAnalyzer.CSharp для более полной проверки.
Install-Package SonarAnalyzer.CSharp

Также его можно добавить как глобальную ссылку в Directory.Build.props:
<ItemGroup>
<PackageReference Include="SonarAnalyzer.CSharp" />
</ItemGroup>

Это в сочетании с такими настройками:
<Project>
<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisLevel>latest</AnalysisLevel>
<AnalysisMode>All</AnalysisMode>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
</Project>

…и ваши сборки будут завершаться неудачей при серьёзных недостатках качества кода. Это может быть отличной подстраховкой. Но поначалу это может мешать. Если некоторые правила не соответствуют вашему контексту, вы можете изменить или отключить их в файле .editorconfig, установив для важности правила значение none.

5. Настройка локальной оркестровки
Для обеспечения согласованности локальной среды в команде вам понадобится оркестровка контейнеров. Есть два основных варианта.

1) Docker Compose
Добавьте поддержку Docker Compose в Visual Studio. Будет добавлен файл docker-compose.yml, в котором вы можете определить сервисы:
services:
webapi:
build: .
postgres:
image: postgres:18
environment:
POSTGRES_PASSWORD: password

Это позволит каждому разработчику локально развернуть один и тот же стек при помощи одной команды.

2) .NET Aspire
.NET Aspire выводит оркестровку на новый уровень. Он обеспечивает обнаружение сервисов, телеметрию и оптимизированную настройку, интегрированные с вашими проектами .NET. Вы можете добавить проект .NET и ресурс Postgres всего несколькими строками кода:
var postgres = builder.AddPostgres("demo-db");

builder.AddProject<WebApi>("webapi")
.WithReference(postgres)
.WaitFor(postgres);

builder.Build().Run();

Aspire также использует Docker, но предоставляет более широкие возможности для разработки.
Не важно, Docker Compose или Aspire, цель одна: воспроизводимая, надёжная локальная конфигурация, которая работает одинаково на всех машинах.

6. Автоматизация сборки
Простой рабочий процесс GitHub Actions для проверки каждого коммита .github/workflows/build.yml:
name: Build

on:
push:
# Выполнение только на ветке main
branches: [main]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
- run: dotnet restore
- run: dotnet build --no-restore --configuration Release
- run: dotnet test --no-build --configuration Release

Это гарантирует, что проект всегда будет собираться и проходить тесты, а проблемы будут выявляться до того, как они попадут в продакшн. Если сборка непрерывной интеграции (CI) завершится неудачей, вы сразу поймете, что что-то не так.

Что касается тестирования, изучите:
- Тестирование архитектуры,
- Интеграционное тестирование с Testcontainers.
Это даст уверенность в том, что код работает как ожидалось в среде, максимально приближенной к производственной.

Источник: https://www.milanjovanovic.tech/blog/6-steps-for-setting-up-a-new-dotnet-project-the-right-way
👍18
День 2458. #ЧтоНовенького
Microsoft Исправила Самую Серьёзную Уязвимость в
ASP.NET Core
На прошлой неделе Microsoft исправили уязвимость, получившую «самый высокий» уровень серьёзности из-за уязвимости безопасности ASP.NET Core.

Эта ошибка подмены HTTP-запросов (CVE-2025-55315) была обнаружена в веб-сервере Kestrel ASP.NET Core. Она позволяет аутентифицированным злоумышленникам подделывать другой HTTP-запрос для кражи учётных данных других пользователей или обхода средств безопасности на стороне клиента.

«Злоумышленник, успешно воспользовавшийся этой уязвимостью, может просматривать конфиденциальную информацию, такую как учётные данные других пользователей (Конфиденциальность), и вносить изменения в содержимое файлов на целевом сервере (Целостность), а также может вызвать сбой на сервере (Доступность)», — заявили в Microsoft во вторник в информационном бюллетене.

Для устранения этой уязвимости Microsoft выпустили обновления безопасности для Microsoft Visual Studio 2022, ASP.NET Core 2.3, ASP.NET Core 8.0 и ASP.NET Core 9.0, а также для пакета Microsoft.AspNetCore.Server.Kestrel.Core для приложений ASP.NET Core 2.x.

Чтобы защитить свои приложения ASP.NET Core от потенциальных атак, Microsoft рекомендует разработчикам и пользователям принять следующие меры:
- Если вы используете .NET 8 или более позднюю версию, установите обновление .NET из Центра обновления Microsoft, затем перезапустите приложение или перезагрузите компьютер.
- Если вы используете .NET 2.3, обновите ссылку на пакет Microsoft.AspNet.Server.Kestrel.Core до версии 2.3.6, затем перекомпилируйте и повторно разверните приложение.
- Если вы используете автономное/однофайловое приложение, установите обновление .NET, перекомпилируйте и повторно разверните.

Как пояснил руководитель технической программы безопасности .NET Барри Дорранс, последствия атак CVE-2025-55315 будут зависеть от целевого приложения ASP.NET, и успешная эксплуатация уязвимости может позволить злоумышленникам войти в систему под другим пользователем (для повышения привилегий), выполнить внутренний запрос (при атаках с подделкой запросов на стороне сервера), обойти проверки на подделку межсайтовых запросов (CSRF) или выполнить атаки с использованием инъекций.

«Мы не знаем, что из этого возможно, потому что всё зависит от того, как вы написали своё приложение. Поэтому мы оцениваем уровень риска, имея в виду наихудший возможный случай — обход функции безопасности, который позволяет изменить привилегии, — сказал Дорранс. — Вероятно ли это? Скорее всего, нет, если только код вашего приложения не делает что-то странное и не пропускает ряд проверок, которые он должен выполнять при каждом запросе. Тем не менее, пожалуйста, обновитесь».

Источник: https://www.bleepingcomputer.com/news/microsoft/microsoft-fixes-highest-severity-aspnet-core-flaw-ever/
👍15
День 2459. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы). Я решил разобрать их тут. Начнём с 4го, поскольку первые 3 приведены в его книге, обзор на которую я недавно выкладывал.

6. Обобщения (дженерики)

«Можете объяснить преимущества использования обобщений в приложениях .NET и привести пример сценария, в котором использование обобщений может значительно улучшить качество и производительность кода?»

Хороший ответ
«Обобщения в .NET предоставляют возможность записывать определения классов, методов, делегатов или интерфейсов с параметром для типа, с которым они работают. Это обеспечивает повторное использование кода и типобезопасность без накладных расходов на упаковку или приведение типов, что часто встречается в необобщенных типах.

Основные преимущества использования обобщений включают в себя:
- Безопасность типов: обобщения позволяют указывать точный тип при создании экземпляра класса или метода, что снижает количество ошибок во время выполнения.
- Повторное использование кода: благодаря использованию параметров типа, обобщённые классы и методы могут работать с любым заданным типом, что позволяет разработчикам писать более гибкий и повторно используемый код.
- Повышение производительности: обобщения устраняют необходимость в упаковке и распаковке при работе с типами значений, что снижает потребление памяти и повышает производительность.

Пример сценария, в котором обобщения значительно повышают качество и производительность кода, — реализация классов-коллекций. Например, без обобщений список из целых чисел, хранил бы их как объекты (тип object), что потребовало бы упаковки. С обобщениями можно создать List<int>, в котором целые числа хранятся как целые числа, а не объекты, что устраняет необходимость в упаковке и распаковке, повышает производительность выполнения и снижает накладные расходы на память.

Подводя итог, можно сказать, что обобщения расширяют функциональность приложений .NET, делая их более эффективными, типобезопасными и гибкими.»


Часто встречающийся неверный ответ
«Я использую обобщения, когда мне нужно обрабатывать разные типы данных одним и тем же методом или когда я хочу, чтобы мой метод был гибким. Это всё равно, что заставить метод работать с любым типом данных».

Этот ответ демонстрирует понимание обобщений на элементарном уровне, но упускает важные детали и преимущества:

- Недостаток конкретики: В ответе расплывчато упоминается гибкость при работе с различными типами данных, но не говорится о важности типобезопасности и преимуществ производительности, обеспечиваемых обобщениями.

- Чрезмерное упрощение: Концепция обобщений сводится просто к «работе с любым типом», что упускает из виду истинное их предназначение — обеспечение повторного использования кода и безопасности в типоспецифичных операциях.

- Непонимание реализации: В этом ответе не проводится различие между использованием обобщений для обеспечения типобезопасности и необобщённым подходом с использованием типов object, что может привести к проблемам с производительностью и не обеспечивает проверку типов во время компиляции, которую обеспечивают обобщения.

Ошибка обычно возникает из-за поверхностного понимания обобщений, когда разработчик знает, что их можно использовать для работы с несколькими типами данных, но не до конца понимает или оценивает основные принципы и преимущества.

См. также Ковариантность и Контравариантность в Обобщениях.

Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👍9
День 2460. #ЗаметкиНаПолях
Встраивание и Структуры в C#

Сегодня рассмотрим встраивание структур в C#. И как оно может оптимизировать производительность несколькими интересными способами.

Встраивание
Встраивание — это оптимизация компилятора, которая заменяет вызов метода его телом. Например:
public int Add(int a, int b) => a + b;

public int CalculateSum(int x, int y)
=> Add(x, y);

Компилятор может оптимизировать его до:
public int CalculateSum(int x, int y)
=> x + y;


Очевидное преимущество здесь в том, что мы избегаем накладных расходов на вызов метода, но это также может увеличить размер кода и негативно сказаться на производительности (поскольку мы копируем тело метода во все места его вызова). Существует атрибут [MethodImpl(MethodImplOptions.AggressiveInlining)], который можно использовать, чтобы подсказать компилятору, что метод следует встраивать, даже если по умолчанию он этого не делает. Это всего лишь подсказка, и JIT всё равно может её проигнорировать. Мы также можем использовать [MethodImpl(MethodImplOptions.NoInlining)], чтобы предотвратить встраивание метода (подсказать JIT, что встраивать его не следует).

Структуры
Неотъемлемой частью структур является то, что они, как правило, передаются по значению. Это означает, что при передаче структуры методу создаётся её копия. Т.е.:
public struct Point
{
public int X;
public int Y;
}
public void MovePoint(Point p)
{
p.X += 10;
p.Y += 10;
}
Point myPoint = new Point { X = 0, Y = 0 };
MovePoint(myPoint);
// myPoint всё ещё { X = 0, Y = 0 }

В идеале структуры лучше сохранять неизменяемыми именно по этой причине. А также делать небольшими, чтобы избежать накладных расходов на копирование. И вот в чём «прелесть»: встраивая функцию, мы «стираем» необходимость копирования структуры в новый стековый фрейм. Т.е. встраивание может фактически удешевить передачу структур.

Посмотрите вот этот пример на sharplab.io. Вам не нужно понимать JIT-код ASM. Но сама разница в объёме кода между InlineVsNonInlineBenchmark.NonInline() и InlineVsNonInlineBenchmark.Inline() показывает, что Inline скорее всего будет работать быстрее. По сути, NonInline приходится много копировать (инструкции vmovdqu и vmovq), в то время как Inline просто считывает свойство, добавляет что-то и возвращает результат.

PS: на самом деле JIT совершает множество гораздо более хитрых оптимизаций вашего кода. На последнем DotNext об этом был хороший доклад Дмитрия Егорова "JIT не волшебство: как он работает и как не мешать". У кого есть доступ, обязательно посмотрите. Остальные - подождите выхода на Youtube.

Источник: https://steven-giesel.com/blogPost/e89d7156-f3fd-4152-b78a-cb908bc43226/inlining-and-structs-in-c
👍8
День 2461. #ЧтоНовенького
Спонсорство на
NuGet.org
Свершилось то, о чём так долго говорили большевики. Похоже, реагируя на волну коммерциализации популярных бесплатных NuGet-пакетов, NuGet.org предложили реализовать спонсорство, которое упрощает для пользователей возможность поддерживать авторов своих любимых пакетов.

NuGet.org теперь позволяет авторам пакетов добавлять URL спонсорства. Эта ссылка отображается в виде значка ❤️ или кнопки Sponsor this package (Спонсировать этот пакет) на странице пакета, направляя пользователей к безопасным и популярным платформам.

Пользователи пакетов теперь смогут поддерживать любимые пакеты. Найдите значок ❤️ Sponsor на NuGet.org (см. картинку выше). Нажмите на него, чтобы посетить страницу спонсорства и внести свой вклад.

Для владельцев пакетов
Чтобы установить спонсорство для пакета:
1. вы должны быть владельцем или совладельцем пакета на NuGet.org;
2. спонсорская ссылка должна быть на одну из одобренных платформ:
- GitHub Sponsors
- Patreon
- Open Collective
- Ko-fi
- Tidelift
- Liberapay

Зайдите в панель управления пакетами и раскройте блок Sponsorship Links (Ссылки Спонсорства). Добавьте ссылку, система автоматически проверит URL. После добавления ссылок значок ❤️ Sponsor появится на странице пакета.

На данный момент поддерживаются только перечисленные платформы, и NuGet.org не собирает никакой статистики или финансовой информации о спонсорстве. Всё это отдано на откуп этим платформам.

Источник: https://devblogs.microsoft.com/dotnet/announcing-sponsorship-on-nugetdotorg-for-maintainer-appreciation/
👍6
День 2462. #ЗаметкиНаПолях
Динамический LINQ. Начало

Вы реализуете простую конечную точку поиска… а затем приходит таска: «А можно отфильтровать по статусу? По последнему входу в систему? Сортировать по любому столбцу?» и т.п. И вот ваш красивый LINQ-запрос превращается в лес if, а каждое изменение означает повторное развёртывание. Знакомо? Рассмотрим динамические предикаты, которые выполняются как настоящий LINQ (поэтому EF Core транслирует их в SQL): как они работают, когда использовать и несколько полезных советов.

Зачем?
- Неограниченные фильтры: интерфейсы администратора, конструкторы отчётов, сохранённые поисковые запросы — пользователи сами выбирают поля/операции.
- Разные тенанты: каждому клиенту нужны немного разные правила.
- Сохранение производительности EF: библиотека преобразует строку в лямбда-выражение и вызывает реальный метод LINQ (Where, OrderBy, …) для IQueryable; EF по-прежнему перекладывает работу на SQL.

Как работает?
С помощью C# Eval Expression вы можете предоставить выражение в виде строки и вызвать динамическое расширение: WhereDynamic, OrderByDynamic, SelectDynamic, FirstOrDefaultDynamic и т.д. Внутри библиотеки оно преобразуется в дерево выражений и вызывается фактический оператор LINQ. Это работает для IEnumerable и IQueryable (включая EF Core).

Мы рассмотрим некоторые самые распространённые варианты использования:
- WhereDynamic — динамическая фильтрация,
- OrderByDynamic/ThenByDynamic — динамическая сортировка,
- SelectDynamic — динамическая проекция,
- FirstOrDefaultDynamic — динамическое извлечение единичных значений.

1. WhereDynamic
До: куча условных блоков
var q = context.Customers.AsQueryable();
if (onlyActive)
q = q.Where(x => x.Status == CustomerStatus.IsActive);
if (since != null)
q = q.Where(x => x.LastLogon >= since);
if (!string.IsNullOrWhiteSpace(search))
q = q.Where(x => x.Name.Contains(search));
var list = await q.OrderBy(x => x.Name).ToListAsync();

После: один динамический предикат
// using System.Linq;
// using Z.Expressions;

string filter = "x => true";
if (onlyActive)
filter += " && x.Status == 0";
if (since != null)
filter += $@" && x.LastLogon >= DateTime.Parse(""{since:yyyy-MM-dd}"")";
if (!string.IsNullOrWhiteSpace(search))
filter += $@" && x.Name.Contains(""{search}"")";

var list = await context.Customers
.WhereDynamic(filter)
.OrderBy(x => x.Name)
.ToListAsync();

Почему это лучше: один конвейер, отсутствие дублирующихся запросов, и вы можете добавлять необязательные критерии, не изменяя форму запроса.

Кстати, не обязательно вставлять литералы в строку. Передайте объект контекста (анонимный тип/словарь/expando/класс) и ссылайтесь на его свойства по имени внутри выражения:
var env = new {
IsActive = CustomerStatus.IsActive,
LastMonth = DateTime.Now.AddMonths(-1)
};

var recentActive = await context.Customers
.WhereDynamic(x => "x.Status == IsActive
&& x.LastLogon >= LastMonth", env)
.ToListAsync();


Окончание следует…

Источник:
https://thecodeman.net/posts/dynamic-linq-in-dotnet
👎24👍6
День 2463. #ЗаметкиНаПолях
Динамический LINQ. Окончание

Начало

2. OrderByDynamic/ThenByDynamic
Даём пользователю возможность отсортировать результаты по любой колонке (из белого списка).
var sort = sortCol switch
{
"LastLogon" => "x => x.LastLogon",
"TotalSpent" => "x => x.TotalSpent",
_ => "x => x.Name"
};

var ordered = await context.Customers
.OrderByDynamic(sort)
.ThenByDynamic("x => x.Id")
.ToListAsync();


3. SelectDynamic
Для сценариев отчётов/экспорта, выдаём только то, что запросил клиент:
// Клиент выбрал: "Id,Name,Country"
var cols = selectedCols.Split(',')
.Select(c => c.Trim())
.Where(c => allowedCols.Contains(c));

var selectExpr = "x => new { "
+ string.Join(", ", cols.Select(c => $"{c} = x.{c}"))
+ " }";

var result = await context.Customers
.WhereDynamic("x => x.Status == 0")
.SelectDynamic(selectExpr)
.ToListAsync();


4. FirstOrDefaultDynamic
Идеален для поиска одного элемента или валидации на основе критерия, определяющегося во время выполнения:
csharp 
var result = await context.Customers
.FirstOrDefaultDynamic(
"x => x.Email == \\\"name@mail.com\\\" && x.Status == 0");


Бонус: Цепочки динамических конвейеров
Если очень нужно выполнить несколько шагов LINQ в одной динамической строке (фильтрация → упорядочивание → выборка → список), есть API Execute:
var env = new { 
IsActive = CustomerStatus.IsActive,
LastMonth = DateTime.Now.AddMonths(-1) };

var result = context.Customers.Execute<IEnumerable>(
"Where(x => x.Status == IsActive && x.LastLogon >= LastMonth)" +
".Select(x => new { x.CustomerID, x.Name })" +
".OrderBy(x => x.CustomerID).ToList()", env);

Это мощный метод, но код получается не особо читаемый.

Варианты использования
1. Администрирование «Конструктор запросов»
- UI генерирует: поле + оператор + значение;
- Бэкенд выбирает разрешённые поля/операции → строит WhereDynamic (и опционально OrderByDynamic);
- Результат: один конвейер запросов, почти бесконечное количество комбинаций.

2. Маркетинг «Конструктор сегментов»
- Сегменты сохраняются как читаемые выражения (например, «Активные, последние 90 дней,>$500,не тестовые аккаунты»);
- Приложение загружает правило → WhereDynamic → сохраняет результаты;
- Результат: правила развиваются без изменения кода или повторного развёртывания.

3. Многопользовательские правила
- Каждый пользователь хранит несколько предикатов (или базовых фильтров разрешения/запрета);
- Они комбинируются во время запроса и применяются динамически;
- Результат: меньше ветвлений/флагов, более чистая модель.

Замечания
- Белый список полей/операторов: не раскрывайте всю модель; предоставьте в UI только разрешённый список.
- Проверка выражений: отклоняйте неизвестные токены/поля перед выполнением.
- IQueryable до конца: применяйте динамические операции перед ToList(), чтобы EF мог преобразовать их в SQL.
- Нормализуйте значения: используйте даты ISO или параметры (объект env) вместо парсинга текста.
- Сохраняйте читаемость: предпочитайте короткие, компонуемые строки; добавляйте вспомогательные методы для частых случаев.

Когда не использовать
Если у вас 2-3 фиксированных фильтра, которые редко меняются, статический LINQ остается самым простым (и вполне приемлемым). Динамический LINQ имеет смысл при росте изменчивости и опциональности.

Источник:
https://thecodeman.net/posts/dynamic-linq-in-dotnet
👍7👎1
День 2464. #ЗаметкиНаПолях
Потокобезопасная Инициализация с Помощью LazyInitializer
Хотя Lazy<T> является основным решением для ленивой инициализации в .NET, существует менее известная альтернатива, которая может быть более эффективной в некоторых сценариях: LazyInitializer.EnsureInitialized.

Этот статический метод обеспечивает потокобезопасную инициализацию с рядом ключевых характеристик:
- Только ссылочные типы: работает только с классами, но не со значениями.
- Обнаружение на основе NULL: использует значение NULL для определения необходимости инициализации.
- Эффективное использование памяти по сравнению с System.Lazy<T>: не требуется дополнительный объект-обёртка.

Метод предлагает несколько перегрузок для различных сценариев:
Sample? _instance = null;

// Инициализация через конструктор по умолчанию
var instance =
LazyInitializer.EnsureInitialized(ref _instance);

// Фабричный метод
var instance =
LazyInitializer.EnsureInitialized(
ref _instance,
() => new Sample()
);

// Фабричный метод вызывается 1 раз, даже если нескольким потокам требуется инициализация
var syncLock = new object();
var instance =
LazyInitializer.EnsureInitialized(
ref _instance,
ref syncLock,
() => new Sample()
);


Замечания по потокобезопасности
По умолчанию LazyInitializer.EnsureInitialized ведёт себя как LazyThreadSafetyMode.PublicationOnly:
Несколько потоков могут создавать экземпляры одновременно. Сохраняется только первое успешное назначение. Остальные экземпляры отбрасываются.
Для строгой потокобезопасности используйте перегрузку с блокировкой синхронизации, чтобы гарантировать, что фабричный метод будет выполнен только один раз.

Внутренняя реализация этого аналогична следующему коду:
var instance = Volatile.Read(ref _instance) ??
Interlocked.CompareExchange(
ref _instance,
new Sample(),
null);


Преимущества
Главное преимущество перед Lazy<T> — вместо хранения объекта-обёртки Lazy<T> вы напрямую используете целевой экземпляр. Также это снижает затраты памяти, экономя одно поле на каждое лениво инициализированное значение.

Источник: https://www.meziantou.net/thread-safe-initialization-with-lazyinitializer.htm
👍12
День 2465.
Расширение Upgrade Assistant Недоступно в VS 17.14

Решили мы всё-таки начать обновляться с нашего .NET Framework до чего-то более современного. 🥳 Глядишь, где-нибудь к 12му .NET может и успеем. Но сейчас не об этом.

Я с удивлением обнаружил, что после последнего обновления в Visual Studio 2022 (версии 17.14) пропал пункт меню Upgrade, который вызывал расширение Upgrade Assistant.

Оказывается, расширение не поддерживается в версиях старше 17.13. Вместо этого появился пункт Modernize с иконкой Copilot. То есть вместо чёткого пошагового обновления предлагается отдать всё на откуп ИИ. Уже странное предложение. Но это ещё не всё. При попытке всё-таки начать обновление через Modernize, вылетает следующее сообщение:
GitHub Copilot app modernization is available exclusively to users on GitHub Copilot Pro, Pro+, Business, and Enterprise plans. For more details please refer to the documentation.
(Модернизация приложения GitHub Copilot доступна исключительно пользователям тарифных планов GitHub Copilot Pro, Pro+, Business и Enterprise. Подробнее см. в документации.)

То есть мало того, что не работает бесплатное работавшее раньше решение, так вместо него предлагается использовать недетерминированные ответы ИИ, да ещё только в платной подписке.

Стоит ли говорить, что у пользователей от такого нововведения серьёзно подгорело, и они стали строчить гневные отзывы в поддержку. Например, только в этом треде 128 постов.

В итоге в Microsoft сдали назад и вернули возможность использовать Upgrade Assistant. Для этого необходимо выполнить несколько шагов:
1. Удалить следующие расширения VS, если они имеются:
- Upgrade Assistant (.NET Upgrade Assistant - Visual Studio Marketplace)
- GitHub Copilot App Modernization – Upgrade for .NET (GitHub Copilot app modernization - upgrade for .NET - Visual Studio Marketplace)
- Azure Migrate Application and Code Assessment for .NET (Azure Migrate application and code assessment - Visual Studio Marketplace).
2. Перейти в Tools -> Options -> Projects and Solutions -> Modernization (Инструменты -> Настройки -> Проекты и Решения -> Модернизация) и установить Enable legacy Upgrade Assistant (Включить устаревший Помощник по Обновлению) в True.
3. Перезагрузить Visual Studio.
👍27
День 2466. #ВопросыНаСобеседовании
Оптимизировал Тормозящий Микросервис и Шокировал Интервьюера

Техническое собеседование проходило как любое другое. Стандартные вопросы и страх, что спросят о чём-то, с чем ты работал года 2 назад. А потом прозвучал убийственный вопрос: «Один из микросервисов работает очень медленно из-за внешних вызовов API. Как его оптимизировать?»

Хм… Это не про заученный алгоритм и не про архитектуру. Надо размышлять как опытный бэкенд.

1. Без паники, сначала диагностика
Каждый интервьюер ожидает, что вы сразу перейдёте к «кэшированию» или «параллельным вызовам». Но я сказал: «Сначала я проверю, действительно ли внешний API является узким местом». Интервьюер приподнял бровь.

Я пояснил. Добавим трассировку (Open Telemetry) для отслеживания длительности каждого внешнего вызова. Посмотрим задержки для каждой зависимости (иногда медленные наша сериализация или парсинг JSON). Посмотрим на 95-й и 99-й процентили времени отклика, т.к., если 1 из 100 вызовов занимает 5 секунд, этого достаточно, чтобы сервис казался медленным. Проверим закономерности повторных попыток. Если API нестабилен и наш сервис повторяет запрос по три раза, мы утраиваем время отклика.

Цель не в том, чтобы исправить ошибку, а в том, чтобы доказать, что я не исправляю её вслепую. Интервьюер кивнул.

2. Оптимизаций на стороне клиента
Если внешний API действительно медленный, перейдём к улучшениям на клиенте:
- Установим разумные тайм-ауты (2–3 секунды), в зависимости от SLA, и быстрый выход при таймауте.
- Настроим повторные попытки с экспоненциальной задержкой и ограничим количество повторных попыток, например: 1 – через 200 мс, 2 – через 400мс, 3 – через 800мс. Если ответа нет – выходим.
- Добавим «выключатель» (Circuit Breaker). Если API продолжит давать сбои, прервём попытки. Нет смысла долбить сломанную конечную точку.

3. Параллелизм и асинхронные вызовы
Вместо последовательного выполнения нескольких внешних вызовов, распараллелим их, используя асинхронные шаблоны вроде класса Parallel или Task.WhenAll(). Теперь общее время всех запросов будет близко к времени самого медленного, а не к сумме всех.

4. Кэширование
Если данные из внешнего API меняются нечасто, кэшируем их. Можно использовать кэш в памяти, распределённый или гибридный и кэшировать ответы, например, на 10 минут, чтобы кэш достаточно часто обновлялся. Когда API недоступен, можно выдавать слегка устаревшие данные из кэша. Но здесь важно настроить стратегии инвалидации, отслеживать утечки памяти и соотношение попаданий и промахов.

5. Пакетирование и агрегация
Возможно, внешний API поддерживает пакетные запросы, так что можно делать 1 запрос вместо нескольких, например, отправлять несколько идентификаторов в одном запросе. Либо периодически скачивать все данные из API и использовать их локально.

6. Пересмотр архитектуры
Если сервис слишком сильно зависит от внешних API, можно рассмотреть событийно-ориентированную архитектуру. Вместо вызова API в реальном времени обрабатывать данные асинхронно через очереди сообщений (Kafka или RabbitMQ). Так основной поток будет реагировать быстро, а фоновые процессы будут извлекать или обновлять внешние данные позже.
Можно внедрить API Gateway, кэширующий или объединяющий несколько ответов внешнего API. Тогда наш микросервис будет обращаться к одной оптимизированной конечной точке вместо десяти разных.

7. Плавная деградация
Иногда нужно просто корректно завершить работу:
- Установить тайм-аут и возвращать кэшированные/резервные данные.
- Регистрировать инциденты, но не допускать сбоя в работе.
Пользователям всё равно, какой API дал сбой. Им важно, чтобы приложение работало.

8. Мониторинг
Добавим метрики, будем отслеживать задержку по каждой зависимости, оповещать о резком увеличении времени отклика, следить за закономерностями при замедлениях.

После того, как я закончил, интервьюер помолчал. А потом сказал: «Да… как-то так мы и решили эту проблему».

Источник: https://blog.stackademic.com/interviewer-i-optimized-a-laggy-microservice-and-shocked-my-interviewer-heres-what-i-did-562cd8f0dcee
Автор оригинала: Shanvika Devi
👍26