Using the .Net Core Template Engine to Create Custom Templates and Projects – infoq.com
A monthly overview of things you need to know as an architect or aspiring architects.View an example We protect
A monthly overview of things you need to know as an architect or aspiring architects.
View an example
We protect your privacy.
Facilitating the Spread of Knowledge and Innovation in Professional Software Development
Git is a popular tool for version control in software development. It is not uncommon to use multiple Git accounts. Correctly configuring and switching Git accounts is challenging. In this article, we show what Git provides for account configuration, its limitations, and the solution to switch accounts automatically based on a project parent directory location.
In this podcast, Vitess CNCF project technical lead Deepthi Sigireddi discusses the architecture of cloud native distributed databases, sharding, replication, and failover. She also talks about what DB developers should consider when choosing distributed databases.
Yingjun Wu discusses the evolution of streaming databases, and the features and design principles that set streaming databases apart from conventional database systems and stream processing engines.
In this podcast Shane Hastie, Lead Editor for Culture & Methods spoke to Esther Derby and David Horowitz about the second edition of the Agile Retrospectives book.
Platform engineering is not just a technical problem to solve nor an end in itself. In this article, I will share key lessons I have learned while building and delivering three platforms over the last two decades from VMware and Stripe to Apollo GraphQL, including where we got stuck, how we unblocked ourselves, and what ultimately led to the right outcomes for our users and the business.
Special Memorial Day Sale with significant discounts of up to 60% off. Register now.
Get practical advice from senior developers to navigate your current dev challenges. Use code LIMITEDOFFERIDSMUNICH24 for an exclusive offer.
Level up your software skills by uncovering the emerging trends you should focus on. Register now.
Your monthly guide to all the topics, technologies and techniques that every professional needs to know about. Subscribe for free.
InfoQ Homepage Articles Using the .Net Core Template Engine to Create Custom Templates and Projects
Leia em Português
Oct 03, 2019 18 min read
by
reviewed by
This article is part of our .NET educational series which explores the benefits of the technology and how it can help not only traditional .NET developers but all technologists that need to bring robust, performant and economical solutions to market.
With the release of .NET Core 3.0, Microsoft has the next major version of the general purpose, modular, cross-platform and open source platform that was initially released in 2016. .NET Core was initially created to allow for the next generation of ASP.NET solutions but now drives and is the basis for many other scenarios including IoT, cloud and next generation mobile solutions. Version 3 adds a number of oft-requested features such as support for WinForms, WPF and Entity Framework 6.
The tooling story changed dramatically with .NET Core, because of its serious emphasis on the command line. This is a great fit for .NET Core's cross-platform, tooling-agnostic image. The dotnet CLI is the entry point to all of this goodness, and it contains many different commands for creating, editing, building, and packaging .NET Core projects. Here, we’ll focus on just one aspect of the dotnet CLI — the dotnet new command.
This command is mainly used to create projects, and you can often create a simple boilerplate project and then forget about it. We’ll look at how to get the most out of this command, by passing arguments to modify the generated projects, and seeing how we can use the command to create files as well as projects. We'll also see that this tool is a full-fledged template engine, and can be used to install custom templates, as well as make personal templates.
So how do you use dotnet new? Let's start at the beginning and work up to the most interesting stuff. To create a simple console application, start up the command line, change directory to a new empty folder (an important step, explained below), and call dotnet new console:
As I mentioned before, make sure you're in a new, empty folder first. By default, dotnet new will create files in the current folder and will not delete anything that's already there. You can make it create a new folder by using the --output option. For example, you could create a project in a new folder called ConsoleApp42 by typing:
At this point, dotnet new has created a new console project and restored NuGet packages — it's all ready to run. But let's take a look at what's been created:
As you can see, you now have a project file based on the name of the output folder. If you wish, you could use the --name parameter to specify a different name:
This would create the project files in a folder called ConsoleApp42, and would use MyNewApp as the name of the console application being created — you'd get MyNewApp.csproj. If you take a look at Program.cs, you'll also see that the name parameter is used to update the namespace:
But, if you take a look at the folder structure of the project you've just created, you might spot something missing — there's no solution file. You've only got a single project, and while this works fine with dotnet run, it will cause problems when you want to add another project. You can easily create one:
This will create a new, empty solution file. Then, it’s another step to add a project to it.
If you created your solution in our demo's root folder, it would look like:
You can also use the dotnet sln command to remove or list projects in a solution. If you want to add or remove references to a project, you need to use the dotnet add
command. I suggest reading Jeremy Miller's article on the extensible dotnet CLI for more details, or type dotnet help sln or dotnet help add.
Adding another project is very easy also, but you must do it in this two-step fashion — create, then add. For example, you could add a test project to your solution:
Adding new files to a project is even easier, mostly thanks to the improvements .NET Core made to MSBuild files. You no longer need to explicitly list C# files in the .csproj file, because they're automatically picked up through wildcards. You just need to create a file in the folder and it will automatically become part of the project. You can create the file manually, but you can also use dotnet new to provide a template file. For example, you could add a test file to your test project using the nunit-test item template:
Speaking of templates, how do you know what templates are available? How can you tell the difference between a project template and an item template? That's a job for dotnet new --list, which outputs a list of available templates:
This lists all templates. You can use the --type parameter to filter this down, using --type project, --type item, or --type other. Project templates will create a project, item templates create a single file, while other is really only useful for the sln template to create a solution file.
The short name (2nd column above) in this list is the name you use in the call to dotnet new (e.g. dotnet new console, dotnet new classlib, dotnet new mvc, etc). Some templates support multiple languages, with the default shown in square brackets (spoiler — it's always C#). You can choose a different language with the --language option, but be careful of the # symbol! Some command-line shells treat this as a comment character and parsing can fail with --language F#. This can be handled by quoting the value – "--language F#".
Finally, each template has one or more tags. These are a way of classifying templates, but aren't currently used as part of the command-line tooling. However, they could be used for grouping or filtering by other hosts. Yes, that's right, the dotnet new template engine can be used in other hosts, such as IDEs. More on that later.
So far, we've just looked at a very simple Hello World console app, and added some tests. Let's take a look at something more interesting. Say you want to create a new ASP.NET project. Looking at the list of templates above, you have a few choices. You could create an empty web project, an MVC project, a project with Angular, or one with React.js. But these are fairly rigid templates. Can you customise these at all? The good news — yes, you can.
Templates can take in parameters that change what's being generated. The -- help command will provide details on the parameters this template understands. Let's start with a simple example:
Here you can see that the classlib template has two parameters:
--framework
to specify what target framework is written to the project file; and --no-restore, to control if NuGet restore is performed when the project is created.
The web templates have similar parameters, but there are many more of them than we have space to list here. Try dotnet new mvc --help to get an idea of what's available. There are parameters to decide what type of authentication you want, whether to disable HTTPS or not, whether to use LocalDB instead of SQLite, and so on. Each of these parameters changes how the template code is generated, either replacing content in files or including/excluding files as appropriate.
While we're talking about help, here are two very useful commands: dotnet help new, which opens a web page on the dotnet new command itself; and dotnet new {template} –help, which shows help for the named template and its parameters.
The real power of the dotnet new command is the ability to add new, custom templates. Even better, templates can be distributed and shared, simply by packing them into a NuGet package and uploading to nuget.org. This makes it very easy to get started with a framework, or automate the boilerplate of creating new projects or project items.
To add a new custom template, use the dotnet new --install {template} command, passing in either the name of a NuGet package, or a file folder for a local template. But how do you find new templates?
One way is to search for the framework you're using and see if templates are available, but that's a bit hit and miss. Fortunately, you can instead visit dotnetnew.azurewebsites.net and search for templates by keywords. There are over 500 templates tracked on the site, which makes it a good discovery resource.
For example, you could install a set of templates for AWS Lambda projects with dotnet new --install Amazon.Lambda.Templates. One very nice feature of installing templates via NuGet packages is that each package can contain more than one template. This AWS Lambda package contains 28 different templates, including a tutorial project.
Of course, if you no longer want to use the template, simply uninstall it with dotnet new --uninstall {package}. The name passed here is the name of the installed template package, and if you're not sure of the name, simply run dotnet new --uninstall to get a list.
You can also create your own custom templates. These don't have to be for popular frameworks, but might be for internal or personal projects. Essentially, if you often find yourself creating a specific folder structure, set of references, or boilerplate files, consider creating project or item templates. Project templates are simply plain text files, including the .csproj files — there's no requirement that the generated templates are .NET Core specific, and they can be made to target any framework.
It’s very easy to create a new template and they are surprisingly easy to maintain. Traditionally, templates that can perform text substitution use a special syntax, like $VARIABLE$ markers that will be replaced when the template is evaluated. Unfortunately, this is usually invalid syntax for the file type, which makes it impossible to run the project to test that the template is correct. This leads to bugs and slow iteration times, and basically, a bit of a maintenance headache.
Fortunately, the designers of the template engine have thought about this, and come up with a much nicer way of working: running templates.
The idea is simple — the template is just plain text files. No special formats, no special markers. So, a C# file is always a valid C# file. If a template wishes to substitute some text, such as replacing a C# namespace for one that's based on the project name, this is handled with simple search and replace. For example, imagine we had a template that looked like this:
The template's JSON configuration defines a symbol that will replace the namespace. The symbol value would be based on the project name, possibly with a built-in transform applied to make sure it only contains valid characters. The symbol would also define the text it was replacing — "RootNamespace." When the template engine processes each file, if it sees "RootNamespace," it will replace it with the symbol value.
This simple search and replace is usually based on a symbol that is based on a parameter, such as the template name, the output name, or an actual custom parameter. But, it's also possible to create symbols based on generators, to create GUIDs, random numbers, or the current timestamp, and so on.
But no template is complete without conditional code — something that's added or removed based on a parameter. How does dotnet new handle this and keep "running templates" as an option? This is actually handled on a per-file-type basis, with some default config built in, and the ability to define your own style for unknown file formats. Essentially, the idea is to use the file-specific pre-processor (such as #if for C# or C++) for those file types that support it, and specially formatted comments for those that don't, such as JSON.
All of the metadata for a template lives in a template.json file. This includes the template's short name, description, author, tags, and supported language. Because a template can only target a single language, it also includes a "group identity" option, which multiple templates can specify, one for each language. The metadata file can also include optional information about source and target of files to be copied or renamed, conditional file copies, substitution symbols, command-line parameters and post-creation actions such a package restore. But by default, the template engine will copy and process all files in the template’s file structure.
The template.json file must be placed at the root of the template's folder structure, in a folder called .template.config. The rest of the folder structure is entirely up to you — the template engine will keep the same folder structure when evaluating the template. In other words, if you add a README.md file to the root of your template's folder structure, then the template engine will create a README.md in the root of the output folder when you call dotnet new. So, if you use --output MyApp, you will get a file called MyApp/README.md.
2 directories, 4 files
To install and test your template, simply call dotnet new --install {template} as you would to install a custom template, but this time, pass in the path to the root of the template folder structure. To uninstall, use dotnet new --uninstall {template}. Again, if you're not sure of what to pass, use `dotnet new --uninstall` to get a full list.
Once you're ready to distribute your template, you can pack it into a NuGet package and upload to nuget.org. You'll need to create a .nuspec file as normal, but with two slight tweaks: add a packageType element and set the name attribute to "Template," then make sure the template folder structure is copied into a folder called "content"
Additionally, it's possible to include multiple templates in a single package — simply create multiple folders under "content" and add a .tempate.config/template.json
for each template.
There are many more options and capabilities in the template.json file, but covering them all is beyond the scope of this article. But, based on all we’ve covered here, you can see that the template engine is very powerful, flexible, and yet fairly straightforward to use. Please check out the Microsoft docs site as well as the wiki for the dotnet/templating GitHub site.
One of the most interesting things about dotnet new is that it’s designed to be used from multiple hosts. The dotnet new CLI tool is simply one host — the template engine itself can be used as an API from other applications. This is great for those of us who prefer to work with an IDE instead of the command line, but still want to be able to easily add custom project templates, something that isn't always easy with an IDE.
We can see this in action in JetBrains Rider. The New Project dialog is powered by the template engine APIs, listing all the available templates, even custom templates. When the user wishes to create a project, the template engine is used to generate the files.
If you look closely, you'll see that Rider has more templates than the .NET CLI. This is because Rider ships extra templates to support .NET Framework and Xamarin projects. The template engine API allows hosts to install templates to a custom location, and can list them from both, meaning Rider will show custom templates installed by dotnet new --install as well as using the Install button in the new project dialog’s "More templates" page. Once reloaded, the new template is shown in the list, just like all the others.
The dotnet new command makes it easy to create new projects and project items. The default set of templates will get you started with building .NET Core applications, either command line or ASP.NET based, and will help create test projects and target other .NET languages. Custom templates can be easily installed to create projects with other requirements, such as a different folder structure or framework dependencies. And the format for custom templates makes it easy to create your own, taking advantage of substitution variables and conditional code, but still keeping the template project compilable and maintainable. Together with the dotnet sln command, as well as the other extensible dotnet CLI commands, the new template engine makes it easy to create and manage projects, project items and solutions consistently, cross platform and directly from the command line.
Matt Ellis is a developer advocate at JetBrains. He has spent over 20 years shipping software in various industries and currently works with IDEs and development tools, having fun with abstract syntax trees and source code analysis. He also works on the Unity support in Rider.
This article is part of our .NET educational series which explores the benefits of the technology and how it can help not only traditional .NET developers but all technologists that need to bring robust, performant and economical solutions to market.
With the release of .NET Core 3.0, Microsoft has the next major version of the general purpose, modular, cross-platform and open source platform that was initially released in 2016. .NET Core was initially created to allow for the next generation of ASP.NET solutions but now drives and is the basis for many other scenarios including IoT, cloud and next generation mobile solutions. Version 3 adds a number of oft-requested features such as support for WinForms, WPF and Entity Framework 6.
A round-up of last week’s content on InfoQ sent out every Tuesday. Join a community of over 250,000 senior developers. View an example
We protect your privacy.
A round-up of last week’s content on InfoQ sent out every Tuesday. Join a community of over 250,000 senior developers. View an example
We protect your privacy.
June 24 – 25, 2024 | BOSTON, MA
Actionable insights to clarify today’s critical dev priorities.
InfoQ Dev Summit Boston, is a two-day conference hosted by InfoQ, focusing on the most critical technical decisions senior software developers face today.
Deep-dive into 20+ technical talks and get transformative learnings from senior software developers navigating Generative AI, security, modern web applications, and more.
Register Now
InfoQ.com and all content copyright © 2006-2024 C4Media Inc.
Privacy Notice, Terms And Conditions, Cookie Policy