In this article, we are going to talk about creating .NET Project Templates.

To download the source code for this article, you can visit our GitHub repository.

Let’s get started.

What are .NET Project Templates?  

We can use .NET Project templates to create ready-to-run projects that make it easy to start with a working set of code. The .NET SDK comes with a set of default templates to cover the essential project types that we may require like the Console Apps, Class Libraries, ASP.NET Core apps, etc.

To see all the available templates, we can use the dotnet new command with the --list option:

dotnet new --list

This will list all built-in templates as well as the custom ones if we have installed any.

However, sometimes we may need to create our project templates for specific contexts. For instance, an organization may have a requirement that all projects that they create need to follow a specific structure and format. The .NET CLI includes a template engine, which can create projects from a specified template. We are going to see how we can create our project template to use with .NET CLI and Visual Studio.

How to Create a .NET Project Template? 

For that, first, let’s prepare a project.

We can start by creating a standard ASP.NET Core Web API project and then customizing it.

That said, let’s create a new project using dotnet CLI:

dotnet new webapi -o codemazeapi

Since we specify the output folder name as codemazeapi, this will create a new folder named codemazeapi and an ASP.NET Core Web API project named codemazeapi inside it. The project will have the structure of a standard ASP.NET Core Web API:

default web api project template while creating .NET Project Templates 

Now we are going to customize this project a bit. First,  let’s remove the WeatherForecastController and the WeatherForecast class. Then let’s add a ValuesController inside the Controller folder with just a GET action method:

public class ValuesController : ControllerBase
{
    public IActionResult Get()
    {
        return Ok("Hi from Code-Maze Web API Template");
    }
}

Finally, in the launchSettings.json file, let’s modify the launchUrl value to api/values. This will make sure the application hits this controller endpoint on start-up.

Note that in this example we are working with .NET 6. If we use a different version, the default templates and source files could be a little different, but we can always customize those to match this example.

Our customized Web API project is ready. The next step is to create a template out of it.

Creating Template from Existing .NET Project

For creating a template from an existing project, we need to create a new folder .template.config in the root of the project and add a template.json file inside it. This file is the most important part of building our custom template. It contains all the key information required for transforming our .NET project into a project template:

{
  "$schema": "http://json.schemastore.org/template",
  "author": "Muhammed Saleem",
  "classifications": [ "Web", "WebAPI", "C#" ],
  "identity": "CodeMazeTemplates.WebAPI",
  "name": "Code-Maze Web API Template",
  "shortName": "codemazeapi",
  "tags": {
    "language": "C#",
    "type": "project"
  }
}
  • $schema– It defines the schema for the JSON file.
  • author– defines the author of the template
  • classifications– This is used to classify and group the templates. It becomes easier to search and find templates based on the values we provide for this property
  • identity– It uniquely identifies the template
  • name– the name of the template when it is listed
  • shortName– a short name that we can use while creating a new project from the template using dotnet new
  • tags– defines a set of tags for the project. Here we specify that it is a project template in the C# language

With this, our custom project template is ready. The next step is to install it.

For installing the project template locally,  we have to run the dotnet new with the --install or -i command from the project root directory:

dotnet new -i .

Notice that we used . argument with the install command to indicate the template is located in the current folder. If our template location is different, we need to specify that folder path instead.

This will install the template package locally:

The following template packages will be installed:
   C:\code-maze-repo\templates\codemazeapi

Success: C:\code-maze-repo\templates\codemazeapi installed the following templates:
Template Name               Short Name   Language  Tags
--------------------------  -----------  --------  -------------
Code-Maze Web API Template  codemazeapi  [C#]      Web/WebAPI/C#

Now if we run the dotnet new --list command, it will list our custom template along with the .NET default templates:

Template Name                                 Short Name           Language    Tags
--------------------------------------------  -------------------  ----------  -------------------------------------
ASP.NET Core Empty                            web                  [C#],F#     Web/Empty
ASP.NET Core gRPC Service                     grpc                 [C#]        Web/gRPC
ASP.NET Core Web API                          webapi               [C#],F#     Web/WebAPI
ASP.NET Core Web App                          razor,webapp         [C#]        Web/MVC/Razor Pages
ASP.NET Core Web App (Model-View-Controller)  mvc                  [C#],F#     Web/MVC
ASP.NET Core with Angular                     angular              [C#]        Web/MVC/SPA
ASP.NET Core with React.js                    react                [C#]        Web/MVC/SPA
ASP.NET Core with React.js and Redux          reactredux           [C#]        Web/MVC/SPA
Blazor Server App                             blazorserver         [C#]        Web/Blazor
Blazor WebAssembly App                        blazorwasm           [C#]        Web/Blazor/WebAssembly/PWA
Class Library                                 classlib             [C#],F#,VB  Common/Library
Code-Maze Web API Template                    codemazeapi          [C#]        Web/WebAPI/C#
Console App                                   console              [C#],F#,VB  Common/Console
...

We have created our .NET project template and installed it locally. The next step is to create a project using that template.

Creating New Projects Using a Custom Template

For creating a new project using our template, we can use the dotnet new command:

dotnet new codemazeapi

This will create a new project based on the codemazeapi template.

Note that the project and solution have the same name as the template. Similarly, it uses the same namespace as defined in the template. Additionally, we can notice that this command did not create a project folder, it just creates the project in the same folder where we ran the above command. In the next section, we’ll look at how to customize all these.

Customizing .NET Project Templates

Even though we have created a custom project template and created a new project from that template, right now it doesn’t do anything exciting other than copying the source files from the original project folder. However, it is possible to customize the template by modifying the template.json file and providing additional parameters while creating projects. Let’s take a look at a few parameters that come out of the box with .NET templates.

The Output Parameter

While creating new projects using dotnet new, it is possible to specify an output folder by using the -o parameter:

dotnet new codemazeapi -o MyFirstAPI

This will create a new output folder and place the project files inside it. It is a good way to organize projects into separate folders, but the rest of the project contents remain as it is. 

The SourceName Parameter

Now let’s take a look at the sourceName parameter. This represents the name of the project and the namespace that it uses. Once we specify this in the template.json file, it acts as a placeholder and we can override that value while creating projects using the -n parameter:

{
...
  "shortName": "codemazeapi",
  "sourceName": "codemazeapi",
  "tags": {
    "language": "C#",
    "type": "project"
  }
}

After making any changes to the template, we need to uninstall and reinstall the template to update it with the latest changes.

For uninstalling the template, we can use dotnet new -u command.  Along with that, we need to either specify the folder of the template or use the . argument if we are running this command from the template’s root folder:

dotnet new -u .

This will uninstall the template. We can install it once again using the dotnet new -i  as we did in the earlier section.

After installing the updated template, let’s create a new project once again. This time, let’s specify the project name argument as well:

dotnet new codemazeapi -n MySecondAPI

This creates a new project as before, but this time we can see it renames the project and solution file with the name that we specified:

project with name specified

Additionally, we can see that the namespace is also updated with the name that we provided.

However, in this case, we can see that the template creates a new project in the folder in which we ran the command and did not create a separate project folder.

To continue on, let’s see how we can create a new project folder based on the project name.

The PreferNameDirectory Property

The preferNameDirectory parameter, once set to true, will match the output folder with the name of the project: 

{
...
  "shortName": "codemazeapi",
  "sourceName": "codemazeapi",
  "preferNameDirectory":true,
  "tags": {
    "language": "C#",
    "type": "project"
  }
}

Let’s uninstall and install the template once again to update it with the latest changes. After that let’s create a new project once again using the updated template:

dotnet new codemazeapi -n MyThirdAPI

This time we can see that it created a new output folder matching the project name and then creates the project files inside it. This is pretty cool and almost matches the capabilities that an inbuilt .NET project template provides.

Creating Custom Parameters for .NET Project Templates

It is possible to configure the templates to accept custom parameters and customize the projects based on that. In this section, we are going to see how to implement that.

For defining custom parameters, we can use the symbols property :

{
...
  "symbols":{
    "domain": {
        "type": "parameter",
        "description": "Your domain.",
        "defaultValue": "mydomain.com",
        "replaces":"{DOMAIN}"
    }
  }
}

In this example, we define just one parameter called domain

Each parameter has these properties:

  • type– This defines the type of symbol, which is the parameter in this case
  • description– Provides a description for the parameter
  • defaultValue– If the user does not provide a value for the parameter, it takes this as the default value
  • replaces– This is a placeholder that we can specify in the template’s source code. While creating projects, the templating engine will replace all instances of this value with user-specified or default values

Along with that, let’s modify the controller to include the {DOMAIN} placeholder:

public class ValuesController : ControllerBase
{
    public IActionResult Get()
    {
        return Ok("Hi from {DOMAIN} Web API Template");
    }
}

To see the parameter in action, let’s uninstall and install the template once again.

For checking the available parameters, we can use the help -h argument:

dotnet new codemazeapi -h

We can see that the custom parameter is now listed as an option.

Code-Maze Web API Template (C#)
Author: Muhammed Saleem
Options:
  -d|--domain  Your domain.
               string - Optional
               Default: {DOMAIN}

Now let’s create a new project and provide the parameter value :

dotnet new codemazeapi -n MyFourthAPI --domain code-maze.com

This will generate a new project and we can see that it replaces the domain placeholder with the value that we provided:

public IActionResult Get()
{
    return Ok("Hi from code-maze.com Web API Template");
}

Of course, if we do not provide the parameter, it will use the default value:

public IActionResult Get()
{
    return Ok("Hi from mydomain.com Web API Template");
}

Adding Optional Contents to a .NET Project Template 

Our next step is to make some parts of the template optional and to give users the ability to choose it.

Let’s add a new method to the ValuesController and make it optional:

#if (EnableContactInfo)
        [Route("about")]
        public IActionResult About()
        {
            return Ok("visit us at https://code-maze.com/about/");
        }
#endif

Here we are using the C# #if preprocessor directive to define an optional section in the template by using the parameter EnableContactInfo. Similarly, while modifying other file types, we need to use the supporting syntax. 

Along with that, let’s add the EnableContactInfo parameter in template.json file:

{
...
  "symbols":{
    "domain": {
        "type": "parameter",
        "description": "Your domain.",
        "defaultValue": "mydomain.com",
        "replaces":"{DOMAIN}"
    },
    "EnableContactInfo": { 
        "type": "parameter", 
        "dataType":"bool", 
        "defaultValue": "false" 
    }
  }
}

We define the datatype as bool to indicate that this accepts a true or false value.

Now let’s add a new controller and see how to make it optional.

To do that, we are going to add a ContactController class:

public class ContactController : ControllerBase
{
    public IActionResult Get()
    {
        return Ok("visit us at https://code-maze.com/contact/");
    }
}

Then let’s modify the template.json file to specify that ContactController should be optional based on EnableContactInfo parameter:

{
...
  "sources": [
    {
      "modifiers": [
        {
          "condition": "(!EnableContactInfo)",
          "exclude": [
            "Controllers/ContactController.cs"
          ]
        }
      ]
    }
  ]
}

Here we add a modifier to the sources element which excludes the ContactController if the EnableContactInfo parameter is set to false. This is a simple condition, but we can build complex logic using operators like &&, ||, !, <,>, =, etc. if required.

Now let’s uninstall and install the template for reflecting it with the latest changes.

After that, let’s create a new project using the template:

dotnet new codemazeapi -n MyFinalAPI --domain code-maze.com --EnableContactInfo true

Note that we set the EnableContactInfo argument to true. This will create the project with two controllers. Additionally, we can see that the ValuesController has two endpoint methods.

However, If we set the EnableContactInfo  to false while creating the project, it will generate a project with just one controller and the ValuesController will have just one endpoint method.

Conclusion

In this article, we have learned how to build a custom .NET template and create projects out of it. Additionally, we have discussed customizing the template further by introducing parameters and optional contents.