Introduction

If you’ve been working with .NET for a while, you know that as a solution grows, managing it can become a headache. Solution files get bloated with unreadable GUIDs, NuGet package versions drift out of sync across projects, and you end up copying and pasting the same XML into every new .csproj file.

Fortunately, the .NET ecosystem has introduced several fantastic tools to declutter repositories and make enterprise-level management significantly easier. In this post, we’ll look at three game-changers for organizing .NET projects:

  • .slnx (modern solution format)

  • Directory.Packages.props (centralized NuGet management)

  • Directory.Build.props (global build configuration)

1. The Evolution of Solution Files: .sln vs .slnx

For decades, the standard Visual Studio solution file has been the .sln file. While it gets the job done, it has a notoriously poor developer experience when it comes to source control.

The Problem with .sln

Open a traditional .sln file in a text editor and you are greeted with obscure, proprietary syntax and endless GUIDs.

Common Issues:

  • Merge Conflicts: Because of the dense format and reliance on GUIDs, resolving a merge conflict in a .sln file is often frustrating and error-prone.

  • Poor Human Readability: It is not designed to be read or edited outside of Visual Studio.

The Solution: .slnx

To address these issues, Microsoft introduced the .slnx (Solution XML) format. It is a modern, lightweight, XML-based alternative to the classic .sln file.

Key Advantages of .slnx

  • Clean and Readable: Uses standard XML. No more obscure GUID mappings.

  • Git-Friendly: Merge conflicts are drastically reduced and much easier to resolve.

  • Simplified Structure: Displays only projects and solution folders, stripping away legacy noise.

  • Smaller File Size: Typically a fraction of the size of traditional .sln files.

2. Centralizing Dependencies with Directory.Packages.props

In multi-project solutions, managing NuGet packages can quickly turn into “version hell.” One project might reference Newtonsoft.Json v12.0 while another uses v13.0. This leads to:

  • Unexpected runtime behavior

  • Bloated outputs

  • Package consolidation warnings

Enter Central Package Management (CPM)

By creating a Directory.Packages.props file at the root of your repository, you can manage all NuGet package versions in one place.

Step 1: Define Package Versions Centrally

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
    <PackageVersion Include="MediatR" Version="12.2.0" />
  </ItemGroup>
</Project>

Step 2: Reference Packages Without Versions in .csproj

<ItemGroup>
  <PackageReference Include="Newtonsoft.Json" />
</ItemGroup>

Advantages of Central Package Management

  • Consistency: Every project uses the exact same package version.

  • Effortless Upgrades: Update a dependency version in one file and the entire solution updates automatically.

  • Cleaner .csproj Files: Project files focus on functionality, not version management.

3. Streamlining Configurations with Directory.Build.props

Just like package versions, build configurations are often duplicated across projects. If you want every project to:

  • Target .NET 8

  • Enable nullable reference types

  • Use the latest C# version

  • Treat warnings as errors

You would normally have to repeat these settings in every .csproj file.

Applying the DRY Principle to MSBuild

Directory.Build.props is automatically discovered by MSBuild. If placed in a project directory (or any parent directory), its properties are imported into all child projects.

By placing a Directory.Build.props file at the repository root, you can define global build settings for all projects.

Example: Directory.Build.props

<Project>
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <LangVersion>latest</LangVersion>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>

    <Authors>Your Name</Authors>
    <Company>Your Company</Company>
  </PropertyGroup>
</Project>

Advantages of Directory.Build.props

  • Zero Boilerplate: New projects automatically inherit your preferred settings.

  • Global Rule Enforcement: Enforce strict compiler rules across the entire team.

  • Easy Maintenance: Changing the target framework requires modifying just one line.

  • Cleaner Project Files: Reduces duplication and improves clarity.

Summary

By combining:

  • .slnx for cleaner solution structure

  • Directory.Packages.props for centralized dependency management

  • Directory.Build.props for global configuration

You transform your repository from a tangled web of duplicated settings and unreadable configuration files into a modern, maintainable, and enterprise-ready codebase.

If you haven’t adopted these tools yet, take a few minutes to implement them in your current project. Your future self — and your teammates — will appreciate the clarity and consistency.