Streamlining Package and Project Management in .NET Apps using Directory.Build.props and Directory.Packags.props

Nirav SoniNirav Soni
3 min read

Overview

Dependency management is an important requirement in any .NET app. Managing dependencies for a single project can be easy. Managing dependencies for multi-project solutions can prove to be difficult as they start to scale in size and complexity.

What's the motivation?

Have you ever used an external NuGet package in your application and overtime, the same package was required to be injected into multiple project files?

To top it off, after a few weeks, you see your package manager flagging a direct or a transitive vulnerability in one of those packages. And you're out there manually updating the same package to the latest version in multiple individual project files? There clearly is a problem here wherein a lot of potentially similar project settings, package references etc. are repeated in each project within a solution.

I myself was in the same situation a few weeks back and I thought to myself, is there a better way to deal with it?

Enter Central Package Management

Starting with NuGet 6.2, you can centrally manage your dependencies in your projects with the addition of a Directory.Packages.props file and an MSBuild property namely ManagePackageVersionsCentrally. The solution described here is a two-fold solution. Let us start with understanding the significance of Directory.Build.props

Using Directory.Build.props

A Directory.Build.props file can reside in the root directory of your .NET project and contain properties that will get applied to every project in the solution. It is useful for enforcing project standards in one spot without needing to configure each project.

Once the settings are placed in Directory.Build.props file, we can omit the same files in all the individual projects in the solution thus ensuring a clean, consistent and optimal implementation across all the projects within same solution

<!-- Sample Directory.Build.props file -->
<Project>
    <PropertyGroup>
        <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
        <TargetFramework>net8.0</TargetFramework>
        <ImplicitUsings>true</ImplicitUsings>
        <Nullable>enable</Nullable>
        <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
        <LangVersion>latest</LangVersion>
    </PropertyGroup>
</Project>

Now that we understand the importance of Directory.Build.props file, let's get back to streamlining the version of the dependencies (packages) in our solution.

Using Directory.Packages.props

Within theDirectory.Packages.props file we could all the packages along with their versions that are required to be used in any of the project. Think of it as one big container where you're specifying specific version of the packages instructing .NET to use the specified version of the package if/when the package is used in the individual projects.

It is important to note that here, we are required to use PackageVersion and not PackageReference

<!-- Sample Directory.Packages.props file -->
<Project>
  <ItemGroup>
    <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
  </ItemGroup>
</Project>

Once this file is added, our updated sample project file would look like this.

<!-- Sample .csproj file -->
<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
     <!-- The exact version of the Newtonsoft.Json's dependency would be taken from Directory.Packages.props file -->
    <PackageReference Include="Newtonsoft.Json" />
  </ItemGroup>
</Project>

Show me the Code :)

Within this Github repository, you could see both the approaches defined.

  • WithoutCPM - This is how a normal solution/project structure looks like.
  • WithCPM - This folder has central package management solution implemented.

As you can see, the approach with Central Package management is clearly a more streamlined implementation promoting reusable, cleaner and a robust way to handle package management in your .NET Apps

References

  • https://learn.microsoft.com/en-us/nuget/consume-packages/Central-Package-Management
  • https://devblogs.microsoft.com/nuget/introducing-central-package-management/
  • https://github.com/ardalis/CleanArchitecture
4
Subscribe to my newsletter

Read articles from Nirav Soni directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nirav Soni
Nirav Soni

Constantly evolving is what makes me tick! I am a life long learner, self-motivated individual with proven leadership qualities in 8 years of experience in designing and developing software for different domains primarily using .NET stack. Throughout my career, I've had privilege to work with clients and companies of diverse nature be it by size, culture and the domain they operate in. I have a solid experience in requirement gathering, analysis, architecting and developing cloud based applications. I am a quick learner with exceptional communication skills committed to deliver timely and quality software coupled with positive attitude and team spirit. I constantly keep on educating, refining and driving myself to constantly be better at what I do I am constantly learning because I never settle I stay calm in adverse situations I focus on high-quality decisions I have worked on software with variety of domains such as People Mobility Management Healthcare BFSI EdTech I enjoy meeting and getting to know people and hearing about perspective. Reach out to me if you want to talk to me about emerging trends of software development or tech in general. Professional Strengths : Software Development | Requirement Gathering | Software Architecture | Agile Development | Product Grooming | SaaS My ToolKit : C# | .NET Core | WebAPI | SQL | Docker | Kubernetes | Serverless | Cloud (Azure, AWS) | DevOps | Terraform