Встраивать бизнес-логику прямо в SQL-запросы?
Это верный способ превратить код в нечто нетестируемое и хрупкое.
Представь, ты делаешь поиск по инвойсам. Нужно фильтровать по:
- номеру инвойса
- имени клиента
- email
- статусу заказа
- названию продукта
И всё это в любых комбинациях.
Самое быстрое, но грязное решение — напихать всю эту логику прямо в SQL-запрос.
И вот теперь все твои бизнес-правила оказались заперты внутри базы.
Тестировать их без реальной базы уже не выйдет.
Альтернатива — Specification Pattern.
Это способ вынести сложную логику формирования запросов в отдельный класс, чтобы можно было её переиспользовать и тестировать.
Как это работает:
Создаёшь класс спецификации, который описывает критерии поиска.
Он принимает фильтры и строит выражение (например, LINQ).
Потом передаёшь эту спецификацию в репозиторий или обработчик запросов.
Этот подход отлично подходит для фильтрации и сложных запросов.
Результат:
- тесты запускаются быстрее
- бизнес-логика понятнее
- код запросов проще поддерживать
Specification Pattern это про то, чтобы держать логику в месте, где её можно понять, протестировать и не проклинать через полгода.🔫
👉 @KodBlog
Это верный способ превратить код в нечто нетестируемое и хрупкое.
Представь, ты делаешь поиск по инвойсам. Нужно фильтровать по:
- номеру инвойса
- имени клиента
- статусу заказа
- названию продукта
И всё это в любых комбинациях.
Самое быстрое, но грязное решение — напихать всю эту логику прямо в SQL-запрос.
И вот теперь все твои бизнес-правила оказались заперты внутри базы.
Тестировать их без реальной базы уже не выйдет.
Альтернатива — Specification Pattern.
Это способ вынести сложную логику формирования запросов в отдельный класс, чтобы можно было её переиспользовать и тестировать.
Как это работает:
Создаёшь класс спецификации, который описывает критерии поиска.
Он принимает фильтры и строит выражение (например, LINQ).
Потом передаёшь эту спецификацию в репозиторий или обработчик запросов.
Этот подход отлично подходит для фильтрации и сложных запросов.
Результат:
- тесты запускаются быстрее
- бизнес-логика понятнее
- код запросов проще поддерживать
Specification Pattern это про то, чтобы держать логику в месте, где её можно понять, протестировать и не проклинать через полгода.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8🔥3👍2
На Хабре вышла статья о том, как с помощью EF Core и PostgreSQL избежать deadlock и oversell при работе со стоками. Автор разбирает подход с сортировкой обновлений по ключам, использованием ExecuteUpdateAsync, повторными попытками (retry), триггерами, очередями и батчами.
В статье есть бенчмарки, примеры кода и ссылка на репозиторий на GitHub.
Читать подробнее: habr.com/ru/articles/955714/
👉 @KodBlog
В статье есть бенчмарки, примеры кода и ссылка на репозиторий на GitHub.
Читать подробнее: habr.com/ru/articles/955714/
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤2
В превью C# 14 появились partial-конструкторы и partial-события.
Каждый из них должен состоять ровно из одного объявления-определения и одного объявления-реализации.
Реализация partial-события обязана содержать add и remove аксессоры.
👉 @KodBlog
Каждый из них должен состоять ровно из одного объявления-определения и одного объявления-реализации.
Реализация partial-события обязана содержать add и remove аксессоры.
Please open Telegram to view this post
VIEW IN TELEGRAM
👎8👍4🤔2
На GitHub появился классный репозиторий с чётко структурированным материалом по system design. В нём собраны:
- основы проектирования крупных систем,
- типичные вопросы и решения для собеседований,
- карточки Anki для запоминания ключевых концепций.
Сохрани, чтобы не потерять: https://github.com/donnemartin/system-design-primer
👉 @KodBlog
- основы проектирования крупных систем,
- типичные вопросы и решения для собеседований,
- карточки Anki для запоминания ключевых концепций.
Сохрани, чтобы не потерять: https://github.com/donnemartin/system-design-primer
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤3
Ты правильно называешь свои DTO?
Какую схему именования выбрать
Когда ты делаешь Web API, твои эндпоинты принимают и отдают данные.
Частая практика — добавлять к таким моделям суффикс Dto
Но вот в чём проблема:
DTO часто смешивают входные и выходные данные в одном классе.
Со временем такие классы разрастаются и становятся непонятными.
Мой вариант: использовать суффиксы Request и Response вместо Dto:
• CreateUserRequest —> для входных данных
• UserResponse —> для выходных
Почему так лучше:
Чёткое назначение —> сразу видно, модель для входа или для выхода.
Масштабируемость —> изменения в Response не ломают Request.
Поддерживаемость —> не надо гадать, что вообще делает UserDto.
Совет: выбери один подход и используй его последовательно во всём проекте.
А ты как называешь свои модели Request/Response или Dto? Делись опытом
👉 @KodBlog
Какую схему именования выбрать
Когда ты делаешь Web API, твои эндпоинты принимают и отдают данные.
Частая практика — добавлять к таким моделям суффикс Dto
Но вот в чём проблема:
DTO часто смешивают входные и выходные данные в одном классе.
Со временем такие классы разрастаются и становятся непонятными.
Мой вариант: использовать суффиксы Request и Response вместо Dto:
• CreateUserRequest —> для входных данных
• UserResponse —> для выходных
Почему так лучше:
Чёткое назначение —> сразу видно, модель для входа или для выхода.
Масштабируемость —> изменения в Response не ломают Request.
Поддерживаемость —> не надо гадать, что вообще делает UserDto.
Совет: выбери один подход и используй его последовательно во всём проекте.
А ты как называешь свои модели Request/Response или Dto? Делись опытом
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10👍8🤣3🔥2
Деревья выражений в C# помогают создавать быстрые и эффективные методы преобразования данных, ускоряя работу с объектами и запросами в Entity Framework Core. В статье раскрываются механизмы мэппинга и построения сложных фильтров через IQueryable.
Читать подробнее: https://habr.com/ru/articles/932110/
👉 @KodBlog
Читать подробнее: https://habr.com/ru/articles/932110/
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤3
Экономим память как профи: 5 продвинутых техник
Если вы работаете с .NET, то знаете, под капотом куча мощных инструментов, про которые редко говорят. Вот 5 API, которые реально экономят память и ускоряют код.
1. CollectionsMarshal.AsSpan() — прямой доступ к списку
Обычно List<T> это удобная, но безопасная обёртка над массивом.
Если нужен Span без копирования:
Работает напрямую с внутренним массивом списка, без ToArray().
Важно: при изменении размера списка Span станет недействительным.
Когда использовать: большие буферы, короткие циклы.
Выигрыш: до 2 раз меньше памяти.
2. CollectionsMarshal.GetValueRefOrNullRef() — доступ к словарю без двойного поиска
Обычный способ вызывает поиск ключа дважды:
Лучше так:
Получаем ссылку прямо на значение без повторного поиска.
Когда использовать: большие словари, горячие циклы.
Выигрыш: примерно в 2 раза быстрее.
3. GC.AllocateUninitializedArray<T>() — массив без обнуления
.NET по умолчанию обнуляет массив:
Если вы всё равно перезаписываете элементы - то это лишняя работа.
Быстрее:
Когда использовать: если массив заполняется сразу.
Выигрыш: ~15% быстрее на больших массивах.
4. ArrayPool<T>.Shared и IMemoryOwner — аренда массивов
Постоянные аллокации убивают GC:
Используем пул:
Когда использовать: сетевые серверы, конвейеры, потоковые операции.
Выигрыш: до 1000 раз меньше аллокаций.
5. ObjectPool<T> — переиспользование дорогих объектов
Создание тяжёлых объектов вроде StringBuilder дорого:
Пулим:
Когда использовать: логирование, сериализация, временные буферы.
Выигрыш: до 4 раз быстрее, в 50 раз меньше памяти.
Если вы когда-нибудь заглядывали в свой профилировщик и задавались вопросом, почему так много времени тратится на сборку мусора или копирование памяти, эти приёмы помогут вам это исправить. Они требуют аккуратности, но дают настоящую производительность.
👉 @KodBlog
Если вы работаете с .NET, то знаете, под капотом куча мощных инструментов, про которые редко говорят. Вот 5 API, которые реально экономят память и ускоряют код.
1. CollectionsMarshal.AsSpan() — прямой доступ к списку
Обычно List<T> это удобная, но безопасная обёртка над массивом.
Если нужен Span без копирования:
using System.Runtime.InteropServices;
var numbers = new List<int> { 1, 2, 3, 4, 5 };
Span<int> span = CollectionsMarshal.AsSpan(numbers);
Работает напрямую с внутренним массивом списка, без ToArray().
Важно: при изменении размера списка Span станет недействительным.
Когда использовать: большие буферы, короткие циклы.
Выигрыш: до 2 раз меньше памяти.
2. CollectionsMarshal.GetValueRefOrNullRef() — доступ к словарю без двойного поиска
Обычный способ вызывает поиск ключа дважды:
if (dict.TryGetValue("foo", out var value)) {
value++;
dict["foo"] = value;
}Лучше так:
using System.Runtime.InteropServices;
ref int valueRef = ref CollectionsMarshal.GetValueRefOrNullRef(dict, "foo");
if (!Unsafe.IsNullRef(ref valueRef))
valueRef++;
Получаем ссылку прямо на значение без повторного поиска.
Когда использовать: большие словари, горячие циклы.
Выигрыш: примерно в 2 раза быстрее.
3. GC.AllocateUninitializedArray<T>() — массив без обнуления
.NET по умолчанию обнуляет массив:
int[] arr = new int[1000];
Если вы всё равно перезаписываете элементы - то это лишняя работа.
Быстрее:
int[] arr = GC.AllocateUninitializedArray<int>(1000);
for (int i = 0; i < arr.Length; i++) arr[i] = i;
Когда использовать: если массив заполняется сразу.
Выигрыш: ~15% быстрее на больших массивах.
4. ArrayPool<T>.Shared и IMemoryOwner — аренда массивов
Постоянные аллокации убивают GC:
for (int i = 0; i < 1000; i++) {
var buffer = new byte[1024];
DoSomething(buffer);
}Используем пул:
using System.Buffers;
for (int i = 0; i < 1000; i++) {
using IMemoryOwner<byte> owner = MemoryPool<byte>.Shared.Rent(1024);
DoSomething(owner.Memory.Span);
}
Когда использовать: сетевые серверы, конвейеры, потоковые операции.
Выигрыш: до 1000 раз меньше аллокаций.
5. ObjectPool<T> — переиспользование дорогих объектов
Создание тяжёлых объектов вроде StringBuilder дорого:
for (int i = 0; i < 100; i++) {
var sb = new StringBuilder(1024);
sb.Append("Hello ").Append(i);
Console.WriteLine(sb.ToString());
}Пулим:
using Microsoft.Extensions.ObjectPool;
var provider = new DefaultObjectPoolProvider();
var pool = provider.CreateStringBuilderPool();
for (int i = 0; i < 100; i++) {
var sb = pool.Get();
sb.Clear();
sb.Append("Hello ").Append(i);
Console.WriteLine(sb.ToString());
pool.Return(sb);
}
Когда использовать: логирование, сериализация, временные буферы.
Выигрыш: до 4 раз быстрее, в 50 раз меньше памяти.
Если вы когда-нибудь заглядывали в свой профилировщик и задавались вопросом, почему так много времени тратится на сборку мусора или копирование памяти, эти приёмы помогут вам это исправить. Они требуют аккуратности, но дают настоящую производительность.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍9
Rider 2025.3 EAP 6 уже доступен!
В этом билде появилась поддержка ASP. NET и обнаружения проблем с базой данных прямо в окне Monitoring🔥
Теперь, когда ты запускаешь или отлаживаешь приложение, мониторинг автоматически определяет:
Медленные или чрезмерно частые запросы к БД
Слишком большие результаты выборок
Медленные действия MVC или обработчики Razor-страниц
…и многое другое
Подробнее — здесь
👉 @KodBlog
В этом билде появилась поддержка ASP. NET и обнаружения проблем с базой данных прямо в окне Monitoring
Теперь, когда ты запускаешь или отлаживаешь приложение, мониторинг автоматически определяет:
Медленные или чрезмерно частые запросы к БД
Слишком большие результаты выборок
Медленные действия MVC или обработчики Razor-страниц
…и многое другое
Подробнее — здесь
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14
This media is not supported in your browser
VIEW IN TELEGRAM
Задумывался, почему приложение ощущается «тормозным», хотя с пропускной способностью вроде всё ок? Дело в том, что latency и throughput описывают два совершенно разных аспекта производительности.
Latency — это задержка на один пакет. То, что ощущает пользователь, когда нажимает кнопку. Это отзывчивость системы. Это время, за которое один запрос проходит путь от сервера до конечного устройства. В это входит время обработки на сервере, ожидание в очереди, распространение по сети, задержка при передаче и последний участок соединения до устройства пользователя.
Throughput — это объём данных в секунду. Не скорость отдельного пакета, а то, сколько пакетов проходит через «трубу» за определённый промежуток времени. Throughput — это ёмкость системы. Высокий throughput означает, что система справляется с нагрузкой, не захлёбываясь.
👉 @KodBlog
Latency — это задержка на один пакет. То, что ощущает пользователь, когда нажимает кнопку. Это отзывчивость системы. Это время, за которое один запрос проходит путь от сервера до конечного устройства. В это входит время обработки на сервере, ожидание в очереди, распространение по сети, задержка при передаче и последний участок соединения до устройства пользователя.
Throughput — это объём данных в секунду. Не скорость отдельного пакета, а то, сколько пакетов проходит через «трубу» за определённый промежуток времени. Throughput — это ёмкость системы. Высокий throughput означает, что система справляется с нагрузкой, не захлёбываясь.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6❤3👏2
C# и LINQ = функциональный коктейль для кодинга
Если объяснить максимально просто — функциональный стиль позволяет писать код так, как ты о нём говоришь.
«Отфильтровать пользователей старше 30»
«Сгруппировать пользователей по школе»
«Показать все имена пользователей»
В отличие от процедурного подхода, где нужно думать и о том, что делать, и как именно это сделать — управлять переменными, добавлять и удалять элементы из списков, сортировать результаты и так далее.
В LINQ больше двух десятков операторов, но чтобы начать, достаточно пары базовых:
Select — выбрать нужные данные
Where — отфильтровать записи
Any — проверить, существует ли хотя бы один элемент
GroupBy — сгруппировать совпадающие элементы
ToList — собрать результат в List<T>
ToDictionary — собрать результат в Dictionary<K,V>
👉 @KodBlog
Если объяснить максимально просто — функциональный стиль позволяет писать код так, как ты о нём говоришь.
«Отфильтровать пользователей старше 30»
«Сгруппировать пользователей по школе»
«Показать все имена пользователей»
В отличие от процедурного подхода, где нужно думать и о том, что делать, и как именно это сделать — управлять переменными, добавлять и удалять элементы из списков, сортировать результаты и так далее.
В LINQ больше двух десятков операторов, но чтобы начать, достаточно пары базовых:
Select — выбрать нужные данные
Where — отфильтровать записи
Any — проверить, существует ли хотя бы один элемент
GroupBy — сгруппировать совпадающие элементы
ToList — собрать результат в List<T>
ToDictionary — собрать результат в Dictionary<K,V>
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥22❤2
Одно небольшое изменение = запрос в 400 раз быстрее
Вот что было сделано, чтобы добиться такого прироста.
Реализовывалась пагинация с курсором через EF и Postgres.
Но нужно было сделать запрос быстрее.
Как ускорить SQL-запрос?
Добавить индекс, и всё должно полететь.
Так ведь?
Не совсем…
Был создан составной индекс по нужным колонкам, чтобы ускорить выборку.
НО ЗАПРОС СТАЛ МЕДЛЕННЕЕ!!!
Пришлось разбираться, почему индекс не используется.
Оказалось, что дело в сравнении кортежей — это и решило проблему.
Но непонятно было, как перенести это в запрос EF Core.
К счастью, провайдер Postgres поддерживает это через кастомную функцию.
👉 @KodBlog
Вот что было сделано, чтобы добиться такого прироста.
Реализовывалась пагинация с курсором через EF и Postgres.
Но нужно было сделать запрос быстрее.
Как ускорить SQL-запрос?
Добавить индекс, и всё должно полететь.
Так ведь?
Не совсем…
Был создан составной индекс по нужным колонкам, чтобы ускорить выборку.
НО ЗАПРОС СТАЛ МЕДЛЕННЕЕ!!!
Пришлось разбираться, почему индекс не используется.
Оказалось, что дело в сравнении кортежей — это и решило проблему.
Но непонятно было, как перенести это в запрос EF Core.
К счастью, провайдер Postgres поддерживает это через кастомную функцию.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍3
Настройка batch size в Entity Framework
По умолчанию для провайдера SQL Server batch size равен 42.
Это значит, что при вставке 100 строк произойдёт 5 отдельных запросов к базе.
Я недавно гонял простой бенчмарк в .NET, и вот что получилось:
даже если уменьшить количество раунд-трипов, batch size 1000 оказался самым медленным, а вот 200 показал себя лучше всего — некий "sweet spot".
Вывод простой: тестируйте на своих сценариях.
Оптимальный размер batch может отличаться в зависимости от типа нагрузки и объёмов данных.
👉 @KodBlog
По умолчанию для провайдера SQL Server batch size равен 42.
Это значит, что при вставке 100 строк произойдёт 5 отдельных запросов к базе.
Я недавно гонял простой бенчмарк в .NET, и вот что получилось:
даже если уменьшить количество раунд-трипов, batch size 1000 оказался самым медленным, а вот 200 показал себя лучше всего — некий "sweet spot".
Вывод простой: тестируйте на своих сценариях.
Оптимальный размер batch может отличаться в зависимости от типа нагрузки и объёмов данных.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
В Entity Framework 10 есть анализатор, который выдает предупреждение, если выполняется конкатенация внутри вызова «сырого» SQL-метода, как показано ниже
👉 @KodBlog
var users = context.Users
.FromSqlRaw("SELECT * FROM Users WHERE [" + fieldName + "] IS NULL");
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤3🥴1
Планирование в Visual Studio теперь доступно в публичной превью-версии
Это обновление добавляет новый способ, с помощью которого Copilot может разруливать сложные, многошаговые задачи. Благодаря Planning Copilot теперь может изучать твой код, разбивать большие задачи на подзадачи и выполнять их пошагово, в итоге ты получаешь более предсказуемые результаты и лучше понимаешь, что именно он делает.
Попробуй в Visual Studio 2022 версии 17.14:
зайди в Tools → Options → Copilot → Enable Planning и включи функцию.
После этого расскажи, что думаешь.
👉 @KodBlog
Это обновление добавляет новый способ, с помощью которого Copilot может разруливать сложные, многошаговые задачи. Благодаря Planning Copilot теперь может изучать твой код, разбивать большие задачи на подзадачи и выполнять их пошагово, в итоге ты получаешь более предсказуемые результаты и лучше понимаешь, что именно он делает.
Мы уже видим обнадёживающие результаты: улучшилась успешность и стабильность работы разных моделей. Но это только начало
Попробуй в Visual Studio 2022 версии 17.14:
зайди в Tools → Options → Copilot → Enable Planning и включи функцию.
После этого расскажи, что думаешь.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1🥴1
6 Шагов для Правильной Настройки Нового .NET-проекта
1. Задай единый стиль кода
Первое, что я добавляю в проект — файл .editorconfig.
Он заставляет всех в команде придерживаться одинакового форматирования и соглашений об именах, чтобы не было разношерстных отступов и случайных неймингов.
Создать его можно прямо в Visual Studio.
Стандартная конфигурация - хороший старт, но можно подстроить под вкусы команды.
Положи файл в корень решения, чтобы все проекты следовали тем же правилам. При желании можно переопределить настройки в отдельных подпапках, добавив туда свой .editorconfig.
Вот два примера .editorconfig, которые можно использовать:
- из репозитория .NET runtime
- вариант для обычных .NET-проектов
2. Централизуй настройки сборки
Дальше я добавляю файл Directory.Build.props в корень решения. Он позволяет задавать настройки сборки, общие для всех проектов.
Пример:
Так .csproj файлы остаются чистыми и единообразными, поэтому не нужно повторять эти свойства в каждом проекте.
Если потом захочешь подключить статические анализаторы или поменять параметры сборки, то делается это в одном месте.
Фишка в том, что .csproj файлы становятся почти пустыми, кроме ссылок на NuGet-пакеты.
3. Централизуй управление пакетами
Когда решение разрастается, версии NuGet-пакетов в разных проектах начинают расходиться и чёрт, это превращается в кошмар😬
Тут помогает централизованное управление пакетами.
Создай в корне файл Directory.Packages.props:
Теперь, когда подключаешь пакет в проекте, указываешь только имя, без версии:
Все версии хранятся централизованно. Обновлять зависимости становится проще, а рассинхрон версий между проектами исчезает.
Если нужно, можно переопределить версию в конкретном проекте.
4. Подключи статический анализ кода
Статический анализ помогает ловить потенциальные баги и следить за качеством кода. В .NET уже есть встроенные анализаторы, но я обычно добавляю SonarAnalyzer.CSharp, ведь он делает проверку глубже.
Установи пакет:
И добавь его как глобальную зависимость в Directory.Build.props:
Дополнительно настрой сборку:
Теперь билд упадет, если будут серьезные проблемы с качеством кода = неплохая страховка.
Если какие-то правила не подходят, можно понизить их уровень до none в .editorconfig.
5. Настрой локальную оркестрацию
Чтобы у всех разработчиков среда была одинаковой, стоит настроить контейнерную оркестрацию.
Один из вариантов это .NET Aspire
.NET Aspire добавляет сервис-дискавери, телеметрию и упрощает конфигурацию. Все это встроено прямо в .NET-проекты.
Пример добавления проекта и Postgres-ресурса:
Aspire под капотом использует Docker, но разработчику с ним работать приятнее.
В любом случае цель одна.
6. Автоматизируй сборку через CI
В конце я настраиваю простой workflow в GitHub Actions, чтобы проверять каждый коммит.
👉 @KodBlog
1. Задай единый стиль кода
Первое, что я добавляю в проект — файл .editorconfig.
Он заставляет всех в команде придерживаться одинакового форматирования и соглашений об именах, чтобы не было разношерстных отступов и случайных неймингов.
Создать его можно прямо в Visual Studio.
Стандартная конфигурация - хороший старт, но можно подстроить под вкусы команды.
Положи файл в корень решения, чтобы все проекты следовали тем же правилам. При желании можно переопределить настройки в отдельных подпапках, добавив туда свой .editorconfig.
Вот два примера .editorconfig, которые можно использовать:
- из репозитория .NET runtime
- вариант для обычных .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>
Теперь, когда подключаешь пакет в проекте, указываешь только имя, без версии:
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
Все версии хранятся централизованно. Обновлять зависимости становится проще, а рассинхрон версий между проектами исчезает.
Если нужно, можно переопределить версию в конкретном проекте.
4. Подключи статический анализ кода
Статический анализ помогает ловить потенциальные баги и следить за качеством кода. В .NET уже есть встроенные анализаторы, но я обычно добавляю 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>
Теперь билд упадет, если будут серьезные проблемы с качеством кода = неплохая страховка.
Если какие-то правила не подходят, можно понизить их уровень до none в .editorconfig.
5. Настрой локальную оркестрацию
Чтобы у всех разработчиков среда была одинаковой, стоит настроить контейнерную оркестрацию.
Один из вариантов это .NET Aspire
.NET Aspire добавляет сервис-дискавери, телеметрию и упрощает конфигурацию. Все это встроено прямо в .NET-проекты.
Пример добавления проекта и Postgres-ресурса:
var postgres = builder.AddPostgres("demo-db");
builder.AddProject<WebApi>("webapi")
.WithReference(postgres)
.WaitFor(postgres);
builder.Build().Run();Aspire под капотом использует Docker, но разработчику с ним работать приятнее.
В любом случае цель одна.
6. Автоматизируй сборку через CI
В конце я настраиваю простой workflow в GitHub Actions, чтобы проверять каждый коммит.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17❤10❤🔥1
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9