.Net 10 project files explained
When setting up a project for the first time it is easy to just let your project template do all the work. Visual Studio’s IDE hides the details of the project file as you are building your project with little need to actually examine its contents. That is, until you find yourself having to configure your project outside of the ‘happy path’. To address this, I have created this post to fully describe the elements of a project file in all its glory. Enjoy!
Below is the deep-dive, “explain-every-item” guide to a modern .NET 10 (.csproj) project file.
.NET project files have evolved into a concise, MSBuild-based structure, but there are many elements—both common and optional—that matter in real-world solutions.
Full Explanation of All Items in a .NET 10 Project File
A typical .NET 10 project file looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OutputType>Exe</OutputType>
<RootNamespace>MyApp</RootNamespace>
<AssemblyName>MyApp</AssemblyName>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<Version>1.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
<ProjectReference Include="..\Domain\Domain.csproj" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<None Include="appsettings.json" CopyToOutputDirectory="Always" />
</ItemGroup>
</Project>
Let’s break down every category of element you will ever see in a .NET 10 .csproj.
1. <Project> Element
<Project Sdk="Microsoft.NET.Sdk">
SDK options
Sdk ValueMeaningMicrosoft.NET.SdkStandard C# project (console, class library, etc.)Microsoft.NET.Sdk.WebASP.NET Core project (adds Kestrel, Razor, static files, etc.)Microsoft.NET.Sdk.WorkerBackground service template (WorkerService)Microsoft.NET.Sdk.RazorRazor / Blazor componentsMicrosoft.NET.Sdk.BlazorWebAssemblyBlazor WebAssembly client
The Sdk modifies:
default references
build steps
generated code
implicit imports
2. <PropertyGroup>
All compile-time configuration.
You can have multiple <PropertyGroup> blocks (conditional or global).
The key items:
Target Framework
<TargetFramework>net10.0</TargetFramework>
Tells MSBuild which .NET runtime the project builds against.
Multi-targeting uses
<TargetFrameworks>(plural).
Nullable
<Nullable>enable</Nullable>
Controls nullable reference types:
enabledisableannotationswarnings
Implicit Usings
<ImplicitUsings>enable</ImplicitUsings>
Automatically inserts common using directives (System, System.IO, etc.).
OutputType
<OutputType>Exe</OutputType>
Options:
ExeWinExeLibrary(default for class libraries)
RootNamespace
<RootNamespace>MyApp</RootNamespace>
Namespace used for generated code.
AssemblyName
<AssemblyName>MyApp</AssemblyName>
Output DLL/EXE filename.
GenerateDocumentationFile
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Creates MyApp.xml for IntelliSense documentation.
TreatWarningsAsErrors
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
You can also target specific warnings:
<WarningsAsErrors>CS8618;CS1591</WarningsAsErrors>
GenerateAssemblyInfo
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
When true, compiler auto-generates AssemblyInfo.cs.
Version / FileVersion / AssemblyVersion
<Version>1.2.3</Version>
<AssemblyVersion>1.2.0.0</AssemblyVersion>
<FileVersion>1.2.3.4567</FileVersion>
Configurations
<Configurations>Debug;Release</Configurations>
Platform
<Platform>x64</Platform>
PublishSingleFile
<PublishSingleFile>true</PublishSingleFile>
Used for single-file publish (self-contained)
Self-contained publish
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
EnablePreviewFeatures
<EnablePreviewFeatures>true</EnablePreviewFeatures>
Required for preview C#/.NET features.
EnableNativeAOT
<PublishAot>true</PublishAot>
Native AOT compilation.
LangVersion
<LangVersion>13.0</LangVersion>
For .NET 10 / C# 13.
3. <ItemGroup>
Contains references, files, analyzers, embedded resources, etc.
PackageReference
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
You can also use PrivateAssets:
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
ProjectReference
<ProjectReference Include="..\Domain\Domain.csproj" />
Adds a dependency on another local project.
Framework Reference
Used in ASP.NET:
<FrameworkReference Include="Microsoft.AspNetCore.App" />
Reference (legacy)
<Reference Include="System.Xml" />
Used mostly for older .NET Framework libraries.
Analyzer
<Analyzer Include="packages\mypackage\analyzers\cs\MyAnalyzer.dll" />
Adds compiler analyzers (Roslyn).
Compile
<Compile Include="Services\*.cs" />
Allows customizing which C# files compile.
None / Content / EmbeddedResource
Common for config files:
<None Include="appsettings.json" CopyToOutputDirectory="Always" />
Options for CopyToOutputDirectory:
NeverAlwaysPreserveNewest
Resource files
<EmbeddedResource Include="Localization\*.resx" />
Razor Component / Blazor items
<RazorComponent Include="Pages\MyComp.razor" />
4. Conditional Properties
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
Allows:
OS-based conditions
Configuration
TargetFramework variations
5. <Import> Elements
Rare in SDK-style projects, but sometimes seen:
<Import Project="custom.targets" />
Adds extra MSBuild logic.
6. Custom Build Targets
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="echo Post build running" />
</Target>
Targets run at various build phases:
BeforeBuild
AfterBuild
BeforePublish
AfterPublish
7. Additional MSBuild Concepts
Properties ($(PropertyName))
Variables used in conditions and targets.
Items (@(ItemName))
Collections of file entries (Compile, None, Content, etc.)
8. Structure of a Professional Clean Architecture .csproj Setup
Domain
No external packages except value-object utilities
Pure C#
Application
MediatR
Validation
Mapping
Infrastructure
EF Core
Repositories
External services
API / Server
ASP.NET Core
DI composition root
OpenAPI/Swagger
In More Detail…
I’ll walk it in layers:
What a project file really is (MSBuild concepts).
Line-by-line annotated example.
Common
<PropertyGroup>properties (what they do, why you’d care).Common
<ItemGroup>items (PackageReference, ProjectReference, etc.).Metadata on items (CopyToOutputDirectory, PrivateAssets, etc.).
Conditions and custom targets.
How this all fits into Clean Architecture / real solutions.
1. What a .NET project file actually is
A .csproj is just an MSBuild project in XML.
MSBuild has a few core concepts:
Properties (scalar values)
Think: string variables.
Accessed as
$(PropertyName)Declared in
<PropertyGroup>...</PropertyGroup>.
Items (collections of things)
Think: a list of files or references.
Accessed as
@(ItemName)Declared in
<ItemGroup>...</ItemGroup>.
Targets
Named sets of steps (tasks) executed during the build.
Example:
Build,Publish,Clean, or your custom one.
Tasks
Actual executable units (e.g.
<Csc />to compile C#,<Copy />to copy files).
SDK imports
Sdk="Microsoft.NET.Sdk"silently imports.props&.targetsthat define defaults for everything.
So your .csproj is essentially:
<Project Sdk="Microsoft.NET.Sdk">
<!-- Properties -->
<PropertyGroup>…</PropertyGroup>
<!-- Items -->
<ItemGroup>…</ItemGroup>
<!-- Optional custom targets -->
<Target Name="...">…</Target>
</Project>
2. A realistic .NET 10 project file, annotated
Let’s take a slightly richer example and then break it down:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OutputType>Exe</OutputType>
<RootNamespace>MyCompany.MyProduct.Api</RootNamespace>
<AssemblyName>MyCompany.MyProduct.Api</AssemblyName>
<LangVersion>preview</LangVersion>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors>CS1591;CS8618</WarningsAsErrors>
<NoWarn>CS1998</NoWarn>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>false</SelfContained>
<PublishSingleFile>false</PublishSingleFile>
<PublishTrimmed>false</PublishTrimmed>
<Version>1.0.0</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<Optimize>true</Optimize>
<DebugType>portable</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Application\Application.csproj" />
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
<None Include="appsettings.Development.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="Build">
<Message Text="PostBuild target running for $(AssemblyName)" Importance="High" />
</Target>
</Project>
We’ll now decode each category of elements carefully.
3. <PropertyGroup> – the important properties explained
3.1 Framework & runtime settings
TargetFramework
<TargetFramework>net10.0</TargetFramework>
Tells MSBuild which runtime you compile against.
For multi-target:
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
OutputType
<OutputType>Exe</OutputType>
How the output is packaged:
Exe→ console/Web/worker app (has an entry point).WinExe→ Windows GUI app (no console window).Library→ class library (DLL).
3.2 C# language & compiler behavior
Nullable
<Nullable>enable</Nullable>
Controls nullable reference type analysis:
disable– no nullable analysis.enable– treat reference types as non-nullable by default.annotations– only annotate reference types, no warnings.warnings– warn on nullability, but don’t require annotations.
You can also set this per-PropertyGroup (e.g. only for specific TFMs).
ImplicitUsings
<ImplicitUsings>enable</ImplicitUsings>
When enabled, the SDK injects a bunch of common
usingdirectives for you.Typically includes namespaces like
System,System.IO,System.Net.Http, etc.disableif you want complete manual control.
LangVersion
<LangVersion>preview</LangVersion>
Controls which C# language version is used.
Options:
Explicit version (
11,12,13.0, etc.)latestpreview
You often pair this with:
EnablePreviewFeatures
<EnablePreviewFeatures>true</EnablePreviewFeatures>
Opts the project into preview language/runtime features, often required for cutting-edge .NET 10 / C# 13 stuff.
3.3 Assembly naming & namespacing
RootNamespace
<RootNamespace>MyCompany.MyProduct.Api</RootNamespace>
Base namespace used for generated code (e.g. Razor-generated classes).
If not set, defaults to the project name.
AssemblyName
<AssemblyName>MyCompany.MyProduct.Api</AssemblyName>
Name of the generated assembly file:
MyCompany.MyProduct.Api.dll.You may override this if folder name ≠ desired assembly name.
3.4 Warnings, errors, and documentation
GenerateDocumentationFile
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Causes compiler to emit XML documentation file (based on
///comments).Output is
$(AssemblyName).xmlnext to the.dll.Useful for NuGet packages and IntelliSense.
TreatWarningsAsErrors
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
All compiler warnings become errors → fail the build.
Great for engineering discipline, but sometimes too strict for legacy code.
WarningsAsErrors
<WarningsAsErrors>CS1591;CS8618</WarningsAsErrors>
Makes specific warnings errors.
Examples:
CS1591– missing XML documentation.CS8618– non-nullable field must contain a non-null value.
This is typically used instead of global TreatWarningsAsErrors when you want to enforce only certain categories.
NoWarn
<NoWarn>CS1998</NoWarn>
Suppresses specific warnings.
e.g.,
CS1998for async method lackingawait(common in stub/test methods).
You can combine:
<NoWarn>CS1591;CS1998</NoWarn>
3.5 Versioning
Version, AssemblyVersion, FileVersion
<Version>1.0.0</Version> <!-- NuGet / product version -->
<AssemblyVersion>1.0.0.0</AssemblyVersion> <!-- CLR binding version -->
<FileVersion>1.0.0.0</FileVersion> <!-- Win32 file version -->
Version:Used for NuGet &
AssemblyInformationalVersion.
AssemblyVersion:Affects binding redirects; changing this can break dependent assemblies.
FileVersion:Shown in file properties; safe to change freely.
In many projects you only set Version and let others default.
3.6 Publish and runtime behavior
RuntimeIdentifier / RuntimeIdentifiers
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
Builds for a specific OS/architecture runtime.
For multiple:
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
SelfContained
<SelfContained>false</SelfContained>
true→ bundle the .NET runtime with your app (no separate install).false→ depend on a system-installed runtime (framework-dependent).
PublishSingleFile
<PublishSingleFile>false</PublishSingleFile>
true→ publish as a single executable file (everything packed inside).Works best with
SelfContained=true, but can also be used with framework-dependent.
PublishTrimmed
<PublishTrimmed>false</PublishTrimmed>
true→ IL linker trims unused code to reduce app size.Requires careful testing; reflection-heavy code (e.g. some ORMs) can break.
PublishAot (Native AOT)
<PublishAot>true</PublishAot>
Ahead-of-time compile to a native binary.
Faster startup, no JIT, smaller single-file in some cases.
Not every library is compatible.
3.7 Debug / release specific properties
Often inside a conditional PropertyGroup:
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<Optimize>true</Optimize>
<DebugType>portable</DebugType>
</PropertyGroup>
Optimize
truein Release,falsein Debug.Enables JIT/compiler optimizations.
DebugType
portablefor portable PDBs (standard now).Affects what debugging info is emitted.
You can also see:
<DebugSymbols>true</DebugSymbols>
4. <ItemGroup> – core items and what they mean
Items are collections with metadata. The important ones you see in SDK-style .csproj:
4.1 PackageReference
<ItemGroup>
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
</ItemGroup>
References a NuGet package.
Include= package ID.Version= package version.
You can add extra metadata:
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
PrivateAssets:all→ this dependency does not flow transitively to projects that depend on you (e.g. test-only tools).
IncludeAssets:Fine-grained control over what parts of the package you consume.
ExcludeAssets:Opposite of
IncludeAssets.
4.2 ProjectReference
<ItemGroup>
<ProjectReference Include="..\Application\Application.csproj" />
</ItemGroup>
Adds a dependency on another project in the solution.
Compilation-wise it’s like adding a reference to its output assembly.
The build determines the correct build order.
You can also add metadata:
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj">
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
</ProjectReference>
4.3 FrameworkReference
Primarily used with Web / Windows / MAUI workloads:
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
Pulls in a shared framework that’s not delivered as a NuGet package (e.g. web host runtime).
Most modern ASP.NET Core projects get these from the SDK implicitly, so you often don’t see them.
4.4 Reference (assembly reference)
More “old school”:
<ItemGroup>
<Reference Include="System.Xml" />
<Reference Include="SomeLegacyLibrary">
<HintPath>..\lib\SomeLegacyLibrary.dll</HintPath>
</Reference>
</ItemGroup>
Direct reference to a DLL instead of a project or NuGet.
HintPathpoints to where the DLL lives.
In pure .NET 10 work you’ll see this less, but it’s common when interoperating with older libraries.
4.5 File classification: Compile, None, Content, EmbeddedResource
Even when you don’t explicitly declare them, the SDK implicitly defines items:
Compile→ C# source files to compile (*.cs).None→ Files included in project but not compiled or treated as content by default.Content→ Files that should be published as part of the app.EmbeddedResource→ Files embedded directly into the assembly.
You override or customize them like:
<ItemGroup>
<None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
<Content Include="wwwroot\**\*.*" />
<EmbeddedResource Include="Resources\Strings.resx" />
</ItemGroup>
For None / Content, common metadata:
CopyToOutputDirectory=Never|Always|PreserveNewestCopyToPublishDirectory= same options but fordotnet publish.
Example:
<None Include="appsettings.json"
CopyToOutputDirectory="PreserveNewest"
CopyToPublishDirectory="PreserveNewest" />
4.6 Analyzer
<ItemGroup>
<Analyzer Include="packages\MyAnalyzers\analyzers\dotnet\cs\MyAnalyzer.dll" />
</ItemGroup>
Adds Roslyn analyzers (code style, warnings, etc.).
Many modern analyzer packages (e.g.
Microsoft.CodeAnalysis.NetAnalyzers) automatically inject these viabuild/buildTransitive.
4.7 Other special item types (depending on workload)
Depending on SDK:
WPF (
Microsoft.NET.Sdk.WindowsDesktop):Page,ApplicationDefinition,Resource,Compilewith XAML-specific metadata.
MAUI:
MauiXaml,MauiImage,MauiFont, etc.
Blazor / Razor:
<RazorGenerate Include="Pages\Index.razor" /> <RazorComponent Include="Components\*.razor" />
Most of the time, these are implicitly created by the SDK and you don’t see them unless you override default behavior.
5. Item metadata (the little attributes that matter a lot)
On any item (PackageReference, None, Content, etc.) you can have metadata—child elements inside:
Example: file copy semantics
<None Include="config\tenant-settings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
Includeattribute: the actual file path or wildcard.Child elements (metadata) define behavior.
Common metadata:
CopyToOutputDirectoryCopyToPublishDirectoryPrivate(for references – whether the DLL is copied locally)Aliases(for references with aliasing)SubType(old Visual Studio usage)Link(display a file as if it lives in a different folder in VS)
Example using Link:
<None Include="..\Shared\GlobalSettings.json">
<Link>Config\GlobalSettings.json</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
6. Conditions & custom targets
6.1 Conditions on properties or item groups
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net10.0'">
<Nullable>enable</Nullable>
</PropertyGroup>
Condition is a little expression language (
'$(PropertyName)' == 'value').Can be applied to:
PropertyGroupItemGroupIndividual
PackageReference, etc.
Example: only reference a package for certain TFMs:
<ItemGroup Condition="'$(TargetFramework)'=='net10.0'">
<PackageReference Include="SomeNet10OnlyPackage" Version="1.0.0" />
</ItemGroup>
6.2 Custom targets
<Target Name="PostBuild" AfterTargets="Build">
<Message Text="PostBuild target running for $(AssemblyName)" Importance="High" />
</Target>
Name= identifier of your target.AfterTargets/BeforeTargets= hook points.Inside, you call Tasks (e.g.
<Copy>,<Exec>,<Message>).
Example: copy migration scripts after build:
<Target Name="CopyMigrations" AfterTargets="Build">
<Copy SourceFiles="@(Content->'%(FullPath)')"
DestinationFolder="$(OutputPath)\Migrations"
SkipUnchangedFiles="true"
Condition=" '%(Content.Extension)' == '.sql' " />
</Target>
7. How this plays with Clean Architecture / multi-project solutions
In a typical Clean Architecture solution:
Domain project (
Domain.csproj)Sdk="Microsoft.NET.Sdk"TargetFramework>net10.0</TargetFramework>Very minimal:
Few
PackageReferences.No references to Infrastructure or external frameworks.
Application project
ProjectReference→ Domain.PackageReference→ MediatR, FluentValidation, etc.
Infrastructure project
ProjectReference→ Application, Domain.PackageReference→ EF Core, Serilog sinks, external APIs.
API / UI / Worker project
Sdk="Microsoft.NET.Sdk.Web"(API/Blazor Server) or Worker.ProjectReference→ Application, Infrastructure.PackageReference→ Web-specific frameworks (Swashbuckle, HealthChecks, etc.).Extra properties for Publish, RuntimeIdentifiers, etc.
The separation of responsibilities in the .csproj world is mostly:
Which project references which (
ProjectReference)Which frameworks are allowed (
PackageReference)How the thing is built and published (
PropertyGroup)
#dotnet #csharp #dotnet10