Section 4 of 6

Project Files and Structure

🎯 What You'll Learn

  • Understanding the .csproj file format
  • Project properties and configuration
  • Target frameworks and SDK versions
  • Managing project references
  • Understanding bin/ and obj/ folders
  • The role of .sln (solution) files
  • Organizing multi-project solutions
  • Best practices for project structure

Anatomy of a .NET Project

Every .NET project has a specific structure. Let's explore what each file and folder does:

Typical Project Structure Text
MyProject/
β”œβ”€β”€ MyProject.csproj       # Project file (MSBuild)
β”œβ”€β”€ Program.cs             # Main entry point
β”œβ”€β”€ bin/                   # Build output (compiled files)
β”‚   └── Debug/
β”‚       └── net8.0/
β”‚           β”œβ”€β”€ MyProject.dll
β”‚           β”œβ”€β”€ MyProject.exe
β”‚           └── ...
β”œβ”€β”€ obj/                   # Intermediate build files
β”‚   └── Debug/
β”‚       └── net8.0/
β”‚           └── ...
└── Properties/            # Optional: Additional settings
    └── launchSettings.json
ℹ️ What to Commit to Git

Commit: .csproj, .cs files, Properties/
Ignore: bin/, obj/ (add to .gitignore)
These folders are auto-generated during build and can be recreated.

The .csproj File: Your Project's Blueprint

The .csproj file is an XML file that defines your project. It tells MSBuild (the build system) how to compile your code.

Modern .csproj Format

Here's what a typical console app's .csproj looks like:

HelloWorld.csproj XML
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

Let's break down each element:

Project Element

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

The Sdk attribute specifies which SDK to use:

SDK Used For
Microsoft.NET.Sdk Console apps, class libraries
Microsoft.NET.Sdk.Web ASP.NET Core web apps, APIs
Microsoft.NET.Sdk.Worker Background services, workers
Microsoft.NET.Sdk.Razor Razor class libraries

PropertyGroup: Project Settings

OutputType

OutputType XML
<OutputType>Exe</OutputType>
  • Exe - Executable application (console app, web app)
  • Library - Class library (DLL only, no .exe)

TargetFramework

TargetFramework XML
<TargetFramework>net8.0</TargetFramework>

Specifies which .NET version your app targets:

  • net8.0 - .NET 8
  • net7.0 - .NET 7
  • net6.0 - .NET 6
  • netstandard2.1 - .NET Standard 2.1 (cross-platform libraries)
πŸ’‘ Multi-Targeting

You can target multiple frameworks using <TargetFrameworks> (plural):
<TargetFrameworks>net8.0;net6.0</TargetFrameworks>
This builds your library for both .NET 8 and .NET 6.

ImplicitUsings

ImplicitUsings XML
<ImplicitUsings>enable</ImplicitUsings>

When enabled, common namespaces are automatically imported. You don't need to write:

Without ImplicitUsings C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

Console.WriteLine("Hello!");
With ImplicitUsings (enabled) C#
// No using statements needed!
Console.WriteLine("Hello!");

Nullable

Nullable XML
<Nullable>enable</Nullable>

Enables nullable reference typesβ€”a C# 8+ feature that helps prevent null reference exceptions:

  • enable - Nullable warnings enabled (recommended)
  • disable - No nullable warnings
  • warnings - Warnings only, no errors
  • annotations - Annotations only, no warnings

Common Project Properties

Here are additional properties you'll commonly use:

Extended .csproj Example XML
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <!-- Basic Settings -->
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    
    <!-- Language Features -->
    <LangVersion>latest</LangVersion>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    
    <!-- Assembly Information -->
    <AssemblyName>InvenTrack</AssemblyName>
    <RootNamespace>InvenTrack.Core</RootNamespace>
    <Version>1.0.0</Version>
    
    <!-- Package Metadata -->
    <Authors>Your Name</Authors>
    <Company>InvenTrack Inc.</Company>
    <Description>InvenTrack inventory management system</Description>
    <Copyright>Copyright Β© 2024</Copyright>
  </PropertyGroup>

</Project>

Key Properties Explained

Property Purpose
LangVersion C# language version (latest, 12, 11, etc.)
AssemblyName Name of the compiled DLL/EXE
RootNamespace Default namespace for new files
Version Assembly version (1.0.0, 2.1.3, etc.)
Authors Package author(s)
Description Package description

Managing Dependencies

When you add NuGet packages, they're listed in your .csproj:

With Package References XML
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
    <PackageReference Include="Serilog" Version="3.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
  </ItemGroup>

</Project>
ℹ️ Package Versions

Exact version: Version="8.0.0"
Minimum version: Version="8.0.*"
Range: Version="[8.0.0, 9.0.0)"
Most projects use exact versions for reproducible builds.

Project References

When building multi-project solutions (like InvenTrack), you reference other projects:

With Project References XML
<ItemGroup>
  <ProjectReference Include="..\InvenTrack.Core\InvenTrack.Core.csproj" />
  <ProjectReference Include="..\InvenTrack.Data\InvenTrack.Data.csproj" />
</ItemGroup>

Understanding bin/ and obj/ Folders

The obj/ Folder

Contains intermediate build files:

  • Temporary compilation artifacts
  • Project dependency graph
  • Restore information

Safe to delete: Run dotnet clean to remove it.

The bin/ Folder

Contains the final build output:

bin/ Structure Text
bin/
β”œβ”€β”€ Debug/                  # Debug configuration
β”‚   └── net8.0/
β”‚       β”œβ”€β”€ MyApp.dll       # Your compiled code
β”‚       β”œβ”€β”€ MyApp.exe       # Windows launcher
β”‚       β”œβ”€β”€ MyApp.pdb       # Debug symbols
β”‚       β”œβ”€β”€ MyApp.deps.json # Dependencies
β”‚       └── MyApp.runtimeconfig.json
└── Release/                # Release configuration
    └── net8.0/
        └── ...

Debug vs Release

Configuration Optimizations Debug Info Use For
Debug Disabled Full Development, debugging
Release Enabled Minimal Production deployment

Build for Release:

Terminal Shell
dotnet build --configuration Release
# or shorthand:
dotnet build -c Release

Solution Files (.sln)

A solution is a container for multiple related projects. For InvenTrack, you might have:

InvenTrack Solution Structure Text
InvenTrack/
β”œβ”€β”€ InvenTrack.sln                  # Solution file
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ InvenTrack.Web/             # ASP.NET Core Web API
β”‚   β”‚   └── InvenTrack.Web.csproj
β”‚   β”œβ”€β”€ InvenTrack.Core/            # Business logic
β”‚   β”‚   └── InvenTrack.Core.csproj
β”‚   └── InvenTrack.Data/            # Database access
β”‚       └── InvenTrack.Data.csproj
└── tests/
    └── InvenTrack.Tests/           # Unit tests
        └── InvenTrack.Tests.csproj

Creating a Solution

Terminal Shell
# Create a new solution
dotnet new sln -n InvenTrack

# Create projects
dotnet new webapi -n InvenTrack.Web -o src/InvenTrack.Web
dotnet new classlib -n InvenTrack.Core -o src/InvenTrack.Core
dotnet new classlib -n InvenTrack.Data -o src/InvenTrack.Data

# Add projects to solution
dotnet sln add src/InvenTrack.Web/InvenTrack.Web.csproj
dotnet sln add src/InvenTrack.Core/InvenTrack.Core.csproj
dotnet sln add src/InvenTrack.Data/InvenTrack.Data.csproj

# Add project references
dotnet add src/InvenTrack.Web reference src/InvenTrack.Core
dotnet add src/InvenTrack.Core reference src/InvenTrack.Data
πŸ’‘ Why Use Solutions?

β€’ Build all projects with one command: dotnet build
β€’ Visual Studio loads the entire solution
β€’ Manage dependencies between projects
β€’ Organize related projects logically

Working with Solutions

Terminal Shell
# Build entire solution
dotnet build

# Restore all projects
dotnet restore

# Run a specific project
dotnet run --project src/InvenTrack.Web

# List projects in solution
dotnet sln list

Best Practices for Project Organization

1. Separation of Concerns

Organize code into logical projects:

  • Web/API: Controllers, endpoints, middleware
  • Core/Domain: Business logic, entities, interfaces
  • Data/Infrastructure: Database access, external services
  • Tests: Unit tests, integration tests

2. Naming Conventions

  • Solution: CompanyName.ProductName.sln
  • Projects: CompanyName.ProductName.Layer.csproj
  • Namespaces: Match folder structure

3. Folder Structure

Recommended Structure Text
InvenTrack/
β”œβ”€β”€ .gitignore
β”œβ”€β”€ README.md
β”œβ”€β”€ InvenTrack.sln
β”œβ”€β”€ src/                    # Source code
β”‚   β”œβ”€β”€ InvenTrack.Web/
β”‚   β”œβ”€β”€ InvenTrack.Core/
β”‚   └── InvenTrack.Data/
β”œβ”€β”€ tests/                  # Tests
β”‚   └── InvenTrack.Tests/
└── docs/                   # Documentation
    └── architecture.md

4. .gitignore for .NET

Always include a .gitignore file:

.gitignore Text
# Build results
bin/
obj/

# User-specific files
*.user
*.suo
*.userosscache

# Visual Studio
.vs/

# Rider
.idea/

# VS Code
.vscode/
πŸ’‘ Quick .gitignore

Create a .NET .gitignore automatically:
dotnet new gitignore

Advanced .csproj Features

Conditional Compilation

Conditional Properties XML
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
  <DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
  <DefineConstants>TRACE</DefineConstants>
  <Optimize>true</Optimize>
</PropertyGroup>

Including/Excluding Files

File Inclusion XML
<ItemGroup>
  <!-- Include all .txt files as content -->
  <Content Include="**\*.txt" />
  
  <!-- Exclude specific files -->
  <Compile Remove="Temp\**\*.cs" />
</ItemGroup>

Custom Build Targets

Custom Targets XML
<Target Name="PrintVersion" BeforeTargets="Build">
  <Message Text="Building version $(Version)" Importance="high" />
</Target>

Key Takeaways

  • The .csproj file defines your project configuration
  • Modern .csproj files are simple and readable (SDK-style)
  • Sdk attribute determines project type (console, web, library)
  • TargetFramework specifies .NET version
  • ImplicitUsings auto-imports common namespaces
  • Nullable enables null safety features
  • bin/ contains build output, obj/ contains intermediate files
  • Both bin/ and obj/ should be in .gitignore
  • Solutions (.sln) organize multiple related projects
  • Use dotnet sln commands to manage solutions
  • Debug builds for development, Release for production
  • Organize projects by separation of concerns
🎯 Next Steps

You now understand how .NET projects are structured and configured! In the next section, we'll explore the dotnet CLI in depthβ€”all the commands you need to create, build, test, and publish your applications.