Creating a project from scratch each time is a challenge and at the same time inspiration because you can try something new 🤩 (new libraries, new practices, etc.). At the project starting point, you have the inspiration to create a project better than the last one. But what if in an organization you have multiple teams with different levels of knowledge and experience in .NET Core. And teams also create projects and components, but does that team build and structure .NET Core project the same as you or the organization expected? And did you thought about the time you spend to add all dependencies you need to cover all quality rules organization has? And only after this task completion you can start to implement business requirements. When we counted that time we spend for all teams in total, we will get a scary number 🤔. Again and again, each developer starting a new project do the same tasks: organize project structure, include dependencies, consulting with a senior developer to get to know how to use those dependencies. Sounds familiar daily routine? Let`s try to improve that part of the development and decrease the time we spend to create a new project using self-made .NET Core project templates. ✨
Using .NET Core project templates we can define project templates including common dependencies, naming convention, and project directory structure. The main goal is to create a new project only with one command dotnet new commonlib -n SimpleProject
. And that command adds all dependencies and structure projects automatically without any manual work. Challenge accepted! 🚀
Let`s try out create .NET Core templates!
Steps:
- Install prerequisite tools
- Templating tool
dotnet new
- Create template
- Test template
- Add custom parameters
- Pack and publish the template
Prerequisites 🔨
Tools you have to install before create this example project:
- .NET Core 3.1 SDK or newer - https://dotnet.microsoft.com/download/dotnet-core
- Visual Studio Code - https://code.visualstudio.com/
Templating tool dotnet new
When start building a new project we use dotnet new
command. This command call .NET Core Template Engine that uses a predefined template to create a project. Using command dotnet new --list
you can view all installed .NET Core templates. Templates are located in %USERPROFILE%\.templateengine
directory. Template Engine is open source and builds on .NET Core.
What I like in .NET Core Template Engine that you do not need to tokenize your template with magic symbols, like CraftBakery.{value-to-replace}
or CraftBakery.%value-to-replace%
, etc. .NET Core Template Engine uses your defined words, find all matches and replace with values you pass to dotnet new
command. The main benefit is when you create your template it compiles and you can run and test that.
Benefits described in Microsoft documentation about .NET Core templates. đź‘Ť
The template engine doesn't require you to inject special tokens into your project's source code.
The code files aren't special files or modified in any way to work with the template engine. So, the tools you normally use when working with projects also work with template content.
You build, run, and debug your template projects just like you do for any of your other projects.
You can quickly create a template from an existing project just by adding a ./.template.config/template.json configuration file to the project.
Create template 🏗️
Creating .NET Core template starts with running the same command as we will create new projects from scratch. We will create template for making .NET Core libraries.
The template we make consists of the following parts:
src
- the directory contains all source codetests
- the directory contains all tests.gitignore
- contains VS Code and Visual Studio Git ignore the definitionREADME.md
- readme file, that contains project description.sln
- project solution file
Usually, when I create .NET Core projects I follow guidelines that David Fowler published on GitHub.
Create template base
To create template parts I use Windows Terminal
. By executing the following commands we will create a template base.
At first, we need to create a directory our template will be placed commonlib
and two subdirectories src
and tests
.
Our template base will consist of two .NET Core projects. One for the library, placed in src
directory, and one for tests (my preference is for xUnit test framework), placed in tests
directory. All projects are added to the solution file .sln
. For each project we created, I recommend deleting all default classes Class1.cs
and UnitTest1.cs
.
And at the end, we create README.md
and .gitignore
files by executing the following commands.
Content for .gitignore
I generated using https://www.toptal.com/developers/gitignore.
After executing all commands we should get a template base structure.
Add template configuration
.NET Core Template Engine uses configuration directory .template.config
. and template configuration file .template.config/template.json
. Template configuration members are described in JSON scheme (http://json.schemastore.org/template). If you use Visual Studio Code to edit template.json
file you will get IntelliSense.
Inside directory commonlib
create template configuration parts, by executing the following commands.
Open newly created template.json
file using Visual Studio Code by File -> Open File
. Â Change template configuration file as follows.
Think more of configuration members are self describable, for more detailed description see Microsoft documentation. In a nutshell, the main members contains author name, template describable name, unique identifier, short command name we will use with dotnet new commonlib
and keyword sourceName
value Tamplate Engine will replace with the value we specified in -n
argument dotnet new commonlib -n SomeName
.
That`s it, now we can try to install and check if our template is working correctly.
Test template 🧪
Before sharing our template with organization teams we should test it. To test it locally on your machine is very simple. You have to install the template and try to use it. After that, you can check if the template creates the project as you expected.
Install template
Installing a template is easy. You have to execute the same dotnet new
command with one argument.
After executing the command you will get the full list of installed templates. In list, you will find also your template commonlib
.
Create a project using the template
To use our created template we execute one command dotnet new commonlib -n SimpleProject
. Â Try to execute that command in a different location to avoid template overriding. After executing the command you have to get the following project structure.
It is important to notice, that when you test your template it is not necessary to reinstall the template after each change. In the template installation step, you have specified directory that points to the template, so all changes you make will be available without any additional moves.
Uninstall template
Uninstalling a template is easy, the same as installing. When uninstalling the template you have to specify the full path to the template.
By executing command dotnet new -u
you will get the full list of templates with detailed information. That information also includes the uninstall command for the template.
Add custom parameters
For a real live template, it is not enough with a one -n
name parameter which maps to sourceName
template member. You also need additional parameters like project author, project description, etc. .NET Core Template Engine allows us to define custom parameters in template.json
configuration. Member symbols
is responsible for that. This member accepts an array of objects with the parameter description. Each parameter could be described with different attributes.
type
- attribute has only one optionparameter
replaces
- keyword value which will be replaced with the value passed to this parameter
Describe custom parameters
For demonstration purposes, we will use two of all attributes type
and replaces
. To add three custom parameters you need to modify .template.config\template.json
configuration file.
We have added three parameters author
, description
, and year
. To check if parameters are defined correctly execute command dotnet new commonlib -h
. This command outputs generated help.
What is interesting that .NET Core Template Engine automatically creates short names for our parameters. đź‘Ť
Use custom parameters 🔧
Also after defining parameters we have to put that values. I choose to add parameter values to library .\src\CraftBakery\CraftBakery.csproj
file.
To test if our template replaces all parameter values we have to execute the command.
If you open SampleProject.csproj
you will see that all values are replaced correctly.
Pack and publish the template
We have tested our template and currently, we are ready to share the template with other developers. Packing and publishing for templates are the same as other codebases you publish in NuGet. The template package can contain a bunch of templates. You can pack all your organization template in one package. This sounds awesome. 🤩
⚠️ Before packing and installing package run dotnet new -u
to uninstall templates we used for testing.
Prepare template package ⚙️
To pack a template you have to create a directory, place in that directory all templates and in the root directory create template project file CraftBakery.Templates.csproj
file.
Using Visual Studio Code modify template package definition in CraftBakery.Templates.csproj
as following.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageType>Template</PackageType>
<PackageVersion>1.0</PackageVersion>
<PackageId>CraftBakery.Templates</PackageId>
<Title>CraftBakery Templates</Title>
<Authors>Valdis Pavlukevics</Authors>
<Description>Templates used for demonstration purpose.</Description>
<PackageTags>dotnet-new;templates;craftbakery</PackageTags>
<TargetFramework>netstandard2.0</TargetFramework>
<IncludeContentInPack>true</IncludeContentInPack>
<IncludeBuildOutput>false</IncludeBuildOutput>
<ContentTargetFolders>content</ContentTargetFolders>
<NoWarn>$(NoWarn);NU5128;NU5115</NoWarn>
</PropertyGroup>
<ItemGroup>
<Content Include="templates\**\*" Exclude="templates\**\bin\**;templates\**\obj\**" />
<Compile Remove="**\*" />
</ItemGroup>
</Project>
The package definition is very simple. The important part of this file is ItemGroup
. This element describes what directories are included in the package and what is excluded.
Pack template into NuGet package 📦
To make a NuGet package with our templates we have to execute one command dotnet pack
.
In the output, we could notice that our .gitignore
file was not included in package. To include that file we need got yellow advice to run pack command with -NoDefaultExcludes
parameter. After executing again dotnet pack -NoDefaultExcludes
we will get our package.
Publish template package
For demonstration purposes, I do not publish my package to NuGet.org. I use locally running NuGet server BaGet. How to run that server locally check this link https://loic-sharma.github.io/BaGet/installation/local/. To publish the template package you have to execute the command dotnet nuget push
.
> dotnet nuget push -s http://localhost:5000/v3/index.json CraftBakery.Templates.1.0.0.nupkg
Install template package
If you use an on-premise hosted NuGet package server then you have to register the package's feed address to your local configuration. Following command registers locally running NuGet server.
> dotnet nuget add source http://localhost:5000/v3/index.json --name local
To remove local package source execute dotnet nuget remove source local
Now you are ready to install all templates your package contains. To install template package you have to execute command dotnet new -i
and specify package unique identifier you added PackageId
element.
Now you are ready to use your template. 🥳
Summary🎄
In this post, we learned how we can unify our experience and knowledge into project templates pack them and publish them. Templates are the same as projects, you can compile them, debug or just take an existing project and make them as a template. Now each team in the organization can build projects faster and do not think about covering quality rules at the starting point, because templates solve a bunch of questions automatically. And we can spend more time coding what we love to do ❤️. If you are interested to dive into templating deeply check the samples repository https://github.com/dotnet/dotnet-template-samples.
Have fun with making templates and perform faster! 🚀
[eof]