C# / .NET Установка и тулчейн .NET: SDK, dotnet CLI, NuGet и структура проекта
0%

Установка и тулчейн .NET: SDK, dotnet CLI, NuGet и структура проекта

Установка и тулчейн .NET

Прежде чем писать код, нужно собрать окружение, которое будет работать одинаково на вашей машине, у коллеги и на CI. В мире .NET это на удивление просто: почти всё, что нужно, лежит внутри одного SDK, а dotnet CLI — единая точка входа для создания, сборки, запуска, тестирования и публикации.

SDK vs Runtime

Различайте две вещи:

  • .NET Runtime — то, что нужно, чтобы запускать уже собранное приложение.
  • .NET SDK — включает runtime плюс компилятор, dotnet CLI, шаблоны и MSBuild. Для разработки ставим именно SDK.

Скачать: dotnet.microsoft.com/download. На Linux/macOS удобно через пакетный менеджер или официальный скрипт. Проверяем:

# Версия установленного SDK
dotnet --version        # напр. 9.0.100

# Полный список установленных SDK и runtime
dotnet --info

# Список установленных SDK
dotnet --list-sdks

global.json — фиксируем версию SDK

Чтобы команда и CI собирали проект одним и тем же SDK, кладём в корень репозитория global.json. Это устраняет класс проблем «у меня собирается, у тебя нет».

{
  "sdk": {
    "version": "9.0.100",
    "rollForward": "latestFeature"
  }
}

rollForward: latestFeature означает «бери 9.0.1xx, но не прыгай на 9.0.2xx» — разумный баланс между стабильностью и патчами.

dotnet CLI: команды, которые вы будете использовать каждый день

# Создать проекты из шаблонов
dotnet new console -o MyApp          # консольное приложение
dotnet new webapi -o MyApi           # ASP.NET Core Web API
dotnet new classlib -o MyApp.Core    # библиотека классов
dotnet new xunit -o MyApp.Tests      # проект тестов xUnit
dotnet new gitignore                 # правильный .gitignore для .NET

# Решение (solution) — контейнер для нескольких проектов
dotnet new sln -n MyApp
dotnet sln add MyApp/MyApp.csproj MyApp.Core/MyApp.Core.csproj

# Зависимости между проектами
dotnet add MyApp/MyApp.csproj reference MyApp.Core/MyApp.Core.csproj

# Пакеты NuGet
dotnet add package Serilog.AspNetCore
dotnet list package                  # что подключено
dotnet list package --outdated       # что устарело
dotnet list package --vulnerable     # что уязвимо (важно для безопасности!)

# Восстановление, сборка, запуск, тесты
dotnet restore
dotnet build -c Release
dotnet run --project MyApp
dotnet test
dotnet watch run                     # hot reload при изменении файлов

dotnet new list покажет все доступные шаблоны. Отдельно отметим dotnet watch — он перезапускает или «горячо» перезагружает приложение при изменениях, что бесценно при разработке веб-сервисов.

Анатомия проекта: .csproj и .sln

Проект описывается файлом .csproj — это XML на основе MSBuild. Современный формат (SDK-style) минималистичен:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <!-- Nullable reference types: включаем ВСЕГДА -->
    <Nullable>enable</Nullable>
    <!-- Неявные using-и для распространённых namespace-ов -->
    <ImplicitUsings>enable</ImplicitUsings>
    <!-- Предупреждения = ошибки: держим код в чистоте -->
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <!-- Версия языка обычно выводится из TFM, но можно зафиксировать -->
    <LangVersion>latest</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
  </ItemGroup>

</Project>

Обратите внимание: файлы .cs не перечисляются вручную — SDK-style проект включает все *.cs в папке автоматически. Это огромное упрощение по сравнению со старым форматом.

.sln — файл решения, группирующий проекты. Существует и новый упрощённый формат .slnx (XML). В IDE вы открываете именно решение.

Central Package Management (CPM)

Как только проектов становится больше одного, версии пакетов начинают расползаться: в одном проекте Serilog 8.0.0, в другом 8.0.1. Это рецепт конфликтов. Решение — Central Package Management: версии задаются в одном месте.

Создаём в корне репозитория Directory.Packages.props:

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
    <PackageVersion Include="FluentValidation" Version="11.9.0" />
    <PackageVersion Include="xunit" Version="2.9.2" />
  </ItemGroup>
</Project>

Теперь в .csproj версия не указывается — только имя:

<ItemGroup>
  <PackageReference Include="Serilog.AspNetCore" />
</ItemGroup>

Единый источник правды по версиям — то, что нужно для воспроизводимых сборок и безопасности. Подробно: learn.microsoft.com/nuget/consume-packages/central-package-management.

Directory.Build.props — общие настройки на всё решение

Аналогично можно вынести общие свойства всех проектов в Directory.Build.props в корне — например, Nullable, TreatWarningsAsErrors, TargetFramework, чтобы не дублировать их в каждом .csproj.

<Project>
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <AnalysisLevel>latest-recommended</AnalysisLevel>
  </PropertyGroup>
</Project>

Восстановление пакетов и безопасность цепочки поставок

dotnet restore тянет пакеты из настроенных источников (nuget.config). Для воспроизводимости и защиты от подмены пакетов включайте lock-файлы и проверку:

# Зафиксировать точные версии транзитивных зависимостей
dotnet restore --use-lock-file   # создаст packages.lock.json

В .csproj можно включить <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>. На CI используйте dotnet restore --locked-mode — сборка упадёт, если lock-файл разошёлся с реальностью. И регулярно гоняйте dotnet list package --vulnerable --include-transitive — это ваша первая линия обороны против известных CVE.

Форматтер и стиль: EditorConfig + dotnet format

Стиль кода в .NET задаётся файлом .editorconfig — его понимают и IDE, и компилятор Roslyn, и CLI. Это единый механизм: правила именования, отступы, порядок using-ов, а также включение/отключение конкретных анализаторов.

# .editorconfig в корне репозитория
root = true

[*.cs]
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true

# Стиль C#
csharp_style_namespace_declarations = file_scoped:warning
csharp_prefer_braces = true:warning
dotnet_style_require_accessibility_modifiers = always:warning

# Правило именования: приватные поля с _префиксом
dotnet_naming_rule.private_fields_underscore.severity = warning
dotnet_naming_rule.private_fields_underscore.symbols  = private_fields
dotnet_naming_rule.private_fields_underscore.style    = underscore_prefix

# Анализаторы: severity конкретных правил
dotnet_diagnostic.CA2007.severity = none   # ConfigureAwait — не для приложений

Форматирование и проверка стиля из CLI:

dotnet format                 # исправить форматирование и стиль
dotnet format --verify-no-changes   # на CI: упасть, если не отформатировано

Roslyn-анализаторы

Компилятор Roslyn имеет расширяемую систему анализаторов, которые ловят баги и антипаттерны на этапе компиляции. Встроенные .NET-анализаторы (CAxxxx) включаются через <AnalysisLevel> и <EnableNETAnalyzers>true</EnableNETAnalyzers>. Популярные сторонние наборы:

  • Meziantou.Analyzer, Roslynator — сотни дополнительных правил.
  • SonarAnalyzer.CSharp — качество и security.
  • StyleCop.Analyzers — жёсткий стиль (если команде нужен).

Подключаются как обычные NuGet-пакеты с PrivateAssets="all" (не утекают в транзитивные зависимости).

IDE и LSP

  • JetBrains Rider — мощнейшая IDE для .NET, кросс-платформенная.
  • Visual Studio (Windows) — эталон, тяжёлый.
  • VS Code + C# Dev Kit — лёгкий вариант, работает через LSP-сервер на базе Roslyn (github.com/dotnet/roslyn) и OmniSharp-наследника.
  • Neovim/Emacs — через csharp-ls или Roslyn LSP.

Любая из них даёт go-to-definition, рефакторинги, инлайн-подсказки анализаторов.

Рекомендуемый layout решения

Индустриальный стандарт — разделять исходники и тесты:

MyApp/
├── global.json
├── Directory.Build.props
├── Directory.Packages.props
├── .editorconfig
├── .gitignore
├── MyApp.slnx
├── src/
│   ├── MyApp.Api/           # точка входа (ASP.NET Core)
│   ├── MyApp.Application/   # бизнес-логика, use-cases
│   ├── MyApp.Domain/        # доменные модели
│   └── MyApp.Infrastructure/# EF Core, внешние сервисы
└── tests/
    ├── MyApp.UnitTests/
    └── MyApp.IntegrationTests/

Такое разделение мы подробно разберём в статье про архитектуру. Пока запомните принцип: домен не зависит ни от чего, инфраструктура зависит от домена, а не наоборот.

Чеклист готового окружения

  • Установлен .NET 8/9 SDK, проверен dotnet --info.
  • Есть global.json с зафиксированной версией SDK.
  • Nullable и TreatWarningsAsErrors включены (лучше в Directory.Build.props).
  • Настроен Directory.Packages.props (CPM).
  • Есть .editorconfig, dotnet format --verify-no-changes в CI.
  • Включены анализаторы, packages.lock.json под контролем версий.

Что дальше

Основы языка — типы, nullable, записи, pattern matching, коллекции, LINQ и модель памяти. Фундамент, на котором строится всё остальное.

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов