In my last post, I created a .NET Core 2.0 console application for IoT devices. But it’s a bit fiddly to create this kind of console project – I have to add in the runtime framework targets manually to the csproj file. It occurred to me that instead of typing:

dotnet new console -n RaspberryPiProject

and having to make changes manually to the csproj file everytime, I’d rather create my own project type called “corepi” and use a command like

dotnet new corepi -n RaspberryPiProject

and not need to make any changes to the csproj file. It would also be even better if I could target different .NET Core Runtime framework versions with by having a switch on the command line like:

dotnet new corepi -n RaspberryPiProject --runtime 2.0.0-beta-002345-00

I’ve found it’s possible to do all this by creating a .NET Core template. You can see the list of templates installed by default if you run the command

dotnet new --list

screenshot.1489967097

First, create the template structure

I described how to create the IoT console template in the previous post – I created a new console project called “coreiot”, and modified the coreiot.csproj file to include a value for RuntimeFrameworkVersion.

I created this project inside a folder called “coreiot_template” which is on my desktop – so inside that “coreiot_template” directory, there’s a folder named “coreiot” with three files:

  • coreiot.csproj
  • NuGet.config
  • Program.cs

In order to make this a standard .NET Core template, I need to create a new folder called “.template.config” at the same level as the coreiot project folder. So the image below shows the contents of “coreiot_template”.

screenshot.1489966671

Inside this new folder I created a file named template.json with the contents shown below:

{
  "author": "Jeremy Lindsay <https://jeremylindsayni.wordpress.com>",
  "classifications": [ "IoT", "Raspberry Pi" ], 
  "name": "Empty .NET Core Raspberry Pi project",
  "identity": "IoT.Core",
  "shortName": "corepi",
  "tags": {
    "language": "C#"
  },
  "sourceName": "coreiot"
}

This file has some meta data about the template:

  • Author: obviously, this contains information about the author.
  • Classification: this helps when searching for templates.
  • Name: this is just the name and description of the template.
  • Identity: this is a unique identifier for the template.
  • ShortName: this is the name of the template which we can use in the command.
  • SourceName: this is probably the most important tag – all instances of this value (in this case, “coreiot” which is the name of the console project I created earlier) will be replaced with the name of the project specified in the creation command.

Install the new template

So now I have a directory on my Desktop named “coreiot_template”, which contains both the “coreiot” directory and the “.template.config” directory. At this point, it’s very easy for me to register this as a new template with the command:

dotnet new -i "C:\Users\Jeremy\Desktop\coreiot_template"

Now if I run the command to list all the templates available (which is)

dotnet new --list

I see a list like the image below:

screenshot.1490044556

You can see there’s now a ninth template, with the shortname “corepi” and a description “Empty .NET Core Raspberry Pi project”.

Using the new template

Using this new template to create a new project is really easy – the command is:

dotnet new corepi -n RaspberryPi3

When I run this command, I have a new folder named “RaspberryPi3”, which has a project file inside it called “RaspberryPi3.csproj”, and this project file has a reference to the “RuntimeFrameworkVersion” which is correct for .NET Core 2.0 IoT projects.

Extending the new template with more complex parameters

I mentioned earlier that I’d ideally like to be able to specify a framework version when creating the new project (though this would be optional). It turns out that this is actually a very simple thing to do.

1. Choose the parameter you’d like to replace

I edited the csproj file and removed the existing runtime framework version with the text “RUNTIME-FRAMEWORK-VERSION” (as shown in red below). This is the text that I’d like to replace with a custom value.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <RuntimeFrameworkVersion>RUNTIME-FRAMEWORK-VERSION</RuntimeFrameworkVersion>
    <RuntimeIdentifiers>win8-arm;ubuntu.14.04-arm;ubuntu.16.04-arm</RuntimeIdentifiers>
  </PropertyGroup>
</Project>

2. Modify the template.json file to replace this parameter with whatever the user specifies

We can use the keyword “symbols” in the template.json file to define the parameter that we would like to replace with a user-specified value. In the code below, it tells the dotnet command to replace any instance of the string “RUNTIME-FRAMEWORK-VERSION” with the value that the user specifies after the “–runtime” switch.

{
  "author": "Jeremy Lindsay <https://jeremylindsayni.wordpress.com>",
  "classifications": [ "IoT", "Console" ], 
  "name": "Empty .NET Core IoT project",
  "identity": "IoT.Core",
  "shortName": "coreiot",
  "tags": {
    "language": "C#"
  },
  "sourceName": "coreiot",
  "symbols": {
    "runtime": {
      "type": "parameter",
      "datatype": "string",
      "replaces": "RUNTIME-FRAMEWORK-VERSION",
      "defaultValue": "2.0.0-beta-001783-00",
      "description": "The .NET Core runtime framework to use."
    }
  }
}

So now with a command (like the one below):

dotnet new corepi -n RaspberryPiProject --runtime 2.0.0-beta-002345-00

creates a csproj file which looks like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <RuntimeFrameworkVersion>2.0.0-beta-002345-00</RuntimeFrameworkVersion>
	<RuntimeIdentifiers>win8-arm;ubuntu.14.04-arm;ubuntu.16.04-arm</RuntimeIdentifiers>
  </PropertyGroup>
</Project>

Summing up

This is a really powerful technique. So when I create a project with content which is generic enough to be re-usable, it’s really easy for me to use the “dotnet new -i” command to make this into a template that I can use again and again. And if I want to make it even more generic by replacing certain text strings with my own customised values, I can use the “symbols” keyword in the template.json file to define the text which will be replaced by parameters I specify.