demo-app/
├─ src
│ ├─ Project1.csproj
│ │ ├─ bin
│ │ ├─ obj
│ │ └─ Demo.cs
│ └─ ...
├─ test
│ ├─ Project1Test.csproj
│ │ ├─ bin
│ │ ├─ obj
│ │ └─ DemoTest.cs
│ └─ ...
├─ Demo.sln
└─ ...
.NET Project Setup - Best Practices
TL;DR
To implement a clean directory layout you can set
UseArtifactsOutput
(.NET Artifacts Output property) or setBaseIntermediateOutputPath
andBaseOutputPath
.
Whether you are starting out with a new project or refactoring a mature codebase, a good project and folder organization makes your life easier.
There are many good articles about how to structure the content of the source
code directory, but what about the intermediate and binary directories? .NET
still has the default convention of mixing source code with the generated
build artifacts, namely putting an obj
(intermediate) and bin
(final binary output) folder inside each project directory. A widely used
structure separates tests from program code, resulting in a hirarchy like this
While it is important to keep the generated output of each project in a
separate folder to avoid build problems there is still the problem that we
have generated and binary content in our source code directories (src
and
test
). Imagine a larger project with 100+ projects inside a solution and
there is some error with the build tooling (while the clean
command became
quite reliable it can still contain defects). Manually cleaning all 100+
folders is a tedious task and takes a lot of time! Another reason are custom
build actions which become less error prone since they normally operate on the
intermediate or binary output reducing also the potential to mistakenly change
source files.
A better layout separates the build artifacts from the sources like this
demo-app/
├─ bin
│ ├─ Project1
│ └─ Project1Test
├─ obj
│ ├─ Project1
│ └─ Project1Test
├─ src
│ ├─ Project1.csproj
│ │ └─ Demo.cs
│ └─ ...
├─ test
│ ├─ Project1Test.csproj
│ │ └─ DemoTest.cs
│ └─ ...
├─ Demo.sln
└─ ...
The location of the bin
and obj
folders can be changed in your .csproj
or by adding a Directory.Build.Props
alongside your .sln
file by setting
the BaseOutputPath
and BaseIntermediateOutputPath
. At the time of this
writing the BaseIntermediateOutputPath
needs to be set before the import of
the Microsoft.Common.Props
otherwise some NuGet assets will still be placed
in the standard directory. You can either change from the implicit to the
explicit project format or use the Directory.Build.Props
file (recommended)
which is always evaluated first.
Directory.Build.Props
file next to .sln
file.<Project>
<PropertyGroup>
<BaseIntermediateOutputPath>$(MSBuildThisFileDirectory)obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
</PropertyGroup>
</Project>
-<Project Sdk="Microsoft.NET.Sdk">
+<Project>
<PropertyGroup>
<BaseIntermediateOutputPath>..\..\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
</PropertyGroup>
+ <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
...
+ <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>