Sharing configurations between project files

EDIT: Updated examples based on comment by Laimis: https://mstdn.social/@laimis/110746925177883695

Basically, a dotnet solution is made up of one or more projects. Each of these projects have a project file, .csproj og .vbproj. If you’ve ever been working on a dotnet project, you’d already know this.

When we, the bureau I’m with, create new projects, we need to remember to add all sorts of configurations. All of them are shared between projects. Configurations like these:

<Project>
	<PropertyGroup>
		<TargetFramework>net7.0</TargetFramework>
		<Nullable>enable</Nullable>
		<GenerateDocumentationFile>true</GenerateDocumentationFile>
		<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
		<ImplicitUsings>enable</ImplicitUsings>
	</PropertyGroup>
</Project>

Further more, we also need to add a reference to, at least, one nuget package: https://www.nuget.org/packages/StyleCop.Analyzers and a reference to a stylecop config file:

<Project>
	<ItemGroup>
		<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.507">
			<PrivateAssets>all</PrivateAssets>
			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
		</PackageReference>
		<AdditionalFiles Include="..\stylecop.json" Link="stylecop.json" />
	</ItemGroup>
</Project>

This is a lot to remember when creating new projects. Especially for new developers or when time is short.

To fix this, we can use a “small hack”.

First, create a new .props file: [solution-folder]/configs/shared.props:

<Project>
	<PropertyGroup>
		<TargetFramework>net7.0</TargetFramework>
		<Nullable>enable</Nullable>
		<GenerateDocumentationFile>true</GenerateDocumentationFile>
		<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
		<ImplicitUsings>enable</ImplicitUsings>
	</PropertyGroup>
	<ItemGroup>
		<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.507">
			<PrivateAssets>all</PrivateAssets>
			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
		</PackageReference>
		<AdditionalFiles Include="..\stylecop.json" Link="stylecop.json" />
	</ItemGroup>
</Project>

And in your .csproj files, remove configurations present in the shared.props file. and add this line:

<Import Project="..\configs\shared.props" />

Now, you project has all of its own configurations, and all of the shared configurations. Including nuget packages:

<Project Sdk="Microsoft.NET.Sdk.Web">

	<Import Project="..\configs\shared.props" />

	<PropertyGroup>
		<UserSecretsId>[secret]</UserSecretsId>
	</PropertyGroup>
	<ItemGroup>
		<PackageReference Include="Azure.Identity" Version="1.9.0" />
		<PackageReference Include="EfCore.SchemaCompare" Version="7.0.0" />
		<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.9" />
		<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.9">
		  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
		  <PrivateAssets>all</PrivateAssets>
		</PackageReference>
		<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.9" />
		<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.9">
		  <PrivateAssets>all</PrivateAssets>
		  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
		</PackageReference>
		<PackageReference Include="Microsoft.Extensions.Azure" Version="1.6.3" />
	</ItemGroup>
</Project>

Why this works? The <import> documentation states the following:

Declares that the contents of another project file should be inserted at this location

It simply just takes the content of the .props file, and inserts it into the .csproj file.

That’s it, really. A small life-improving, “hack” for dotnet projects.