Simplify microservice implementation using custom .NET templates

Giovanni Lorenzi
TUI Tech Blog
Published in
11 min readApr 29, 2022

Overview

Nowadays more and more modern applications are designed following the microservice architecture pattern.

There are a lot of benefits related to this approach, but there are also drawbacks. One of the drawbacks to this approach is the possible overhead due to the time needed to create the initial setup for each service, which removes time developers need to concentrate on real feature implementation.
Moreover, since those services are independent by definition and can be implemented in parallel by different teams, in some cases it could be hard to maintain coding standards and project structure.

The goal of this article is to show how .NET templates work, and how to create custom ones in order to simplify and standardize new service implementation inside a company.

.NET templates

When you install the .NET SDK on your machine, you automatically receive a lot of basic templates which could be used to create new projects or parts of them.
If you want to see which templates are already installed on your PC you can run the command dotnet new --list. The output will be similar to the following:

> dotnet new --list
These templates matched your input:
Template Name Short Name Language Tags
------------------------- ------------- ---------- ------------
ASP.NET Core Empty web [C#],F# Web/Empty
ASP.NET Core Web API webapi [C#],F# Web/WebAPI
ASP.NET Core Web App (MVC) mvc [C#],F# Web/MVC
ASP.NET Core with Angular angular [C#] Web/MVC/SPA
Class Library classlib [C#],F#,VB Common/Library
Console App console [C#],F#,VB Common/Console
dotnet gitignore file gitignore Config
...

As you can see there are different kinds of templates, which could be used to create a whole project (e.g: console, webapi) or simply a configuration (e.g: gitignore).
These templates are obviously the same you would see during new project creation using an IDE like Visual Studio.

In addition to the ones provided by the .NET SDK it is possible to retrieve a lot of other templates from the web (e.g: AWS), usually delivered as NuGet packages (.nupkg).

Most of the time these templates contain minimal project configurations. It’s quite common for the teams inside a company to have a set of basic configurations and standard libraries which should be used for services. As a result, developers may have to write a lot of code at the start of each project, or even copy it from existing ones, which can lead to time loss and risks introducing errors or misalignment in the structure or configuration.

To avoid these kinds of problems it is possible to create custom templates, defining the project standards for a team or an entire company.

Defining custom .NET templates

The definition of a custom template consists of two parts:

  • the actual content of the template (source code, project files, scripts, …)
  • the template configuration file

Let’s look at a simple WebAPI service. Using one of the default templates, with the command dotnet new webapi --name WebApiTemplate, we obtain the following project structure:

The structure is simple. Now suppose we want to define a more complex custom template for our company, based on this one.
Let’s change the folder structure (grouping controllers, services and models related to the same resource), refactor the startup based on our needs (e.g: using a log formatter provided by a NuGet package), introduce filters to handle custom exceptions and create a test project in which we also define a couple of unit and integration tests.

Once we’re done with the project content it is necessary to define the project configuration file. This one needs to have a fixed name template.json and to be placed inside a folder called .template.config inside the root of the project, as follows:

WebApiTemplate
├── .template.config
│ └── template.json # The template configuration file
├── src
│ ├── WebApiTemplate.csproj
│ └── ...
├── tests
│ ├── WebApiTemplate.Tests.csproj
│ └── ...
└── WebApiTemplate.sln

Inside it we have to define a lot of information regarding the template creation and also the rules related to its future usage.
In addition to some identification data, it is possible to define custom input parameters for the users who will use the template, inclusion/exclusion file rules, scripts to execute after the project creation and more (see here for full template.json references).

Let’s look at a possible base configuration for this file, related to the example project we’ve just created:

The meaning of the parameters in this file are the following:

  • $schema: the template json file schema
  • author: the author of the template
  • classifications: zero or more characteristics which could be used to filter templates when you search them. These values are shown in the tags section of dotnet new --list
  • name: the human readable name for the template
  • identity: the unique identifier for the template
  • shortName: shown as Short Name in dotnet new --list. It is the name to specify when you want to create a new project from a template
  • tags: other useful tags used to improve search results
  • sourceName: the text to replace with the name specified by the user via the -n|--name flag. The engine replaces this text in all the files inside the template folder
  • preferNameDirectory: define whether to create a directory with the same name of the project, in which the actual project will be placed

Once the file has been created it is possible to install the template locally and test it, in order to verify that it works as expected.
Execute the command dotnet new -i|--install TEMPLATE_PATH, specifying the path of the template to install (i.e: where the folder .template.config is located).

> dotnet new — install WebApiTemplate/.Success: WebApiTemplate installed the following templates:
Template Name Short Name Language Tags
--------------- ------------- -------- -----------------------
.NET 6 WebAPI tuimm.webapi [C#] TUI Musement/Web/WebAPI

From now on the template will be locally available and will be shown in the list of all the installed templates

> dotnet new --list
These templates matched your input:
Template Name Short Name Language Tags
------------------- ------------- -------- ------------
NET 6 WebAPI tuimm.webapi [C#] TUI Musement/Web/WebAPI
ASP.NET Core Empty web [C#],F# Web/Empty
ASP.NET Core Web API webapi [C#],F# Web/WebAPI
dotnet gitignore gitignore Config
...

Let’s now try to use it to create a new project:

> dotnet new tuimm.webapi --name MyFirstWebApi
The template “.NET 6 WebAPI” was created successfully.

The value defined with the--name parameter has been used to replace the original name WebApiTemplate (sourceName in the configuration file). This way all occurrences of that name in all the files inside the project folder have been replaced with MyFirstWebApi. This works for the solution and project files as well as for namespaces inside the source code and all the other references (e.g: in the Dockerfile).

Template customization

In the previous example we’ve seen how a simple template works and its configuration. Let’s now suppose we want to allow the user to enable/disable some kind of feature during the project generation. How do we define those rules inside the code and the template configuration file?
Let’s make an example of three of the (probably) most common scenarios:

  • Exclude one or more files
  • Change a default value
  • Add/remove a piece of code

Exclude one or more files

By default, all the files inside the template folder will be part of the template we’re creating. This means that when someone creates a new project with this template, it will have all the same files. Sometimes it’s useful to define some rules to allow the final user to choose if he wants to remove some of them. Let’s assume, for example, that we want to give the user the possibility to choose whether he wants to generate the Dockerfile or not.
In this case we should edit the template.json, adding the following lines

By doing this we’re defining a custom boolean parameter, named CreateDokerfile, which has default value true. If the user decides to set its value to false the source modifier defined in this configuration will exclude the file src/Dockerfile from the project generation. You can see the result in the following example:

> dotnet new tuimm.webapi --name MySecondWebApi --CreateDockerfile falseThe template “.NET 6 WebAPI” was created successfully.

Change a default value

If we want to give the user the possibility to change a default value inside the template, regardless of whether it is inside the source code, a script or the readme, we should add a parameter with the following properties:

Here the string value passed as HealthEndpoint will be used to edit the default health check endpoint inside the code:

> dotnet new tuimm.webapi --name MyThirdWebApi --HealthEndpoint /healthcheckThe template “.NET 6 WebAPI” was created successfully.

This is a good solution when the default value is replicated in more files (e.g: in source code and inside the documentation). The final user will enter the value just once, during the project creation, and won’t have to worry about manually replacing the value in all the files.

Add/remove a piece of code

Sometimes changing a default value is not enough and we may want to give the user more flexibility to generate different project content (e.g: adding or removing blocks of code).
In this case, we have to work on both the configuration file and the source code.
Let’s assume we decide to make the Swagger generation enabled by default and give the user the possibility to disable it.
First of all, we need to define a boolean parameter in the configuration file:

Then we have to edit the source code in the following way (note that this is the same syntax used for compiler directives):

Moreover, since the Swagger generation is performed using a library provided by a NuGet package we may want to conditionally exclude that package from the project. In this case the directives to be used inside the .csproj file have a different syntax:

Trying to create a new project disabling the Swagger generation will cause the following result:

> dotnet new tuimm.webapi --name MyFourthWebApi --EnableSwagger falseThe template “.NET 6 WebAPI” was created successfully.

As you can see some lines of code have been omitted, and the NuGet package is no longer included in the project.

Show custom parameters in IDE

By default, these parameters are available only through the CLI and can be seen by typing the help command (e.g: dotnet new tuimm.webapi -h). This means that if we would like to create a new project with an IDE (e.g: Visual Studio) we would not be able set any value for these parameters. To allow this it is necessary to create another file, named ide.host.json, to be placed inside the template configuration folder:

WebApiTemplate
├── .template.config
│ ├── template.json # The template configuration file
│ └── ide.host.json # The IDE configuration file
├── src
│ ├── WebApiTemplate.csproj
│ └── ...
├── tests
│ ├── WebApiTemplate.Tests.csproj
│ └── ...
└── WebApiTemplate.sln

The json file structure in this case is simple:

  • $schema: the json file schema. In this case the value related to the configuration for Visual Studio
  • icon: define a custom icon for the template. It should be placed in the same folder as the json file
  • symbolInfo: list of user defined parameters:
    - id: the identifier of the parameter. It should match the value defined in the symbols list in the file described above
    - name: the text to be shown as the parameter description in the project configuration form
    - isVisible: boolean value which specifies whether the custom parameter will be shown or not on the IDE. The default value is false

The obtained result in Visual Studio is the following:

Pack templates as NuGet packages

So far we’ve seen how to create a custom template, how to add parameters and rules to dynamically configure the project creation and how to install and use it locally. Since templates are created to be used by multiple developers, it is necessary to find a simple solution to deliver them. This goal can be reached by packaging the templates into NuGet package (.nupkg).

To do that we must first create a file templatepack.csproj and a folder structure like the following (note: we can pack multiple templates inside one package):

pack
├── templates
│ └── WebApiTemplate
│ │ ├── .template.config
│ │ │ └── ...
│ │ └── ...
│ └── Template2
│ │ ├── .template.config
│ │ │ └── ...
│ │ └── ...
│ └── Template3
│ ├── .template.config
│ │ └── ...
│ └── ...
└── templatepack.csproj # The package project file

The content of the .csproj file will be the following:

Most of the parameters in this file have self-explanatory names. The most important one is PackageId, which is the package identifier that will be used as the package name.

Once we’ve created this file, we can proceed to generate the NuGet package by typing the command dotnet pack:

> dotnet packMicrosoft (R) Build Engine version 17.1.0+ae57d105c for .NET
Copyright © Microsoft Corporation. All rights reserved.
Determining projects to restore...
Restored C:\pack\templatepack.csproj (in 73 ms).
templatepack -> C:\pack\bin\Debug\net6.0\templatepack.dll
Successfully created package ‘C:\pack\bin\Debug\TUIMusement.Templates.1.0.0.nupkg’.

The template installation will work as described in the previous section, with the only difference that we need to specify the package file instead of the template path:

> dotnet new -i bin\Debug\TUIMusement.Templates.1.0.0.nupkgThe following template packages will be installed:
C:\pack\bin\Debug\TUIMusement.Templates.1.0.0.nupkg
Success: TUIMusement.Templates::1.0.0 installed the following templates:
Template Name Short Name Language Tags
-------------- ------------- --------- --------------------
.NET 6 WebAPI tuimm.webapi [C#] TUI Musement/Web/WebAPI

Once we’ve tested the package installation locally, we can share it by uploading it on nuget.org. After that process, the templates will be available to all the developers who can then download and install them through the Package Manager.

Conclusions

In this guide we’ve shown what .NET templates are, how to use them to speed up project initialization and, the most important thing, how to create custom ones and deliver them in order to simplify standardization among one or more teams.

In this repository you can find the source code of the project used for the examples, with the template configuration file.

Below you can find some useful links, in case you want to explore this topic deeper:

--

--