Authoring Visual Studio Templates with NuGet Dependencies
NuGet supports adding NuGet packages to Visual Studio project templates and item templates. When Visual Studio creates a project using a template that contains these NuGet packages, the resulting project will have these packages installed. This is why this feature is often referred to as the "Preinstalled NuGet Packages" feature.
This is useful when you want your project/item template to reference a library (e.g. jQuery or EntityFramework). For example, project templates for ASP.NET MVC 3 include jQuery, jQuery.Validation, Modernizr, and a number of other libraries as Nuget packages.
This provides a better experience for end-users who can easily update the NuGet packages installed by the project/item template long after the template has shipped. At the same time it makes it easier to author templates since you only need to reference a single library package file instead of tracking all the files required by the library
Authoring Visual Studio templates
This article does not describe how to author a Visual Studio template. Follow these links to find more information on how to create templates directly using Visual Studio or using the Visual Studio SDK.
You should author your template such that it does not include the
file or any references or content that installing the NuGet package(s) would add to
the project. In the next section, you'll use a custom wizard that will add those
files and references to the project as part of template expansion.
Adding packages to a template
Preinstalled packages work using template wizards. A special wizard gets invoked when the template gets instantiated. The wizard loads the list of packages that need to be installed and passes that information to the appropriate NuGet APIs.
The template needs to specify where to find the package nupkg files. Currently three package repositories are supported:
- Packages embedded inside of a VSIX package.
- Packages embedded inside of the project/item template itself.
- Packages installed on hard drive, with a registry key referencing the path.
To add preinstalled packages to your project/item template you need to:
Edit your vstemplate file and add a reference to the NuGet template wizard by adding a
<WizardExtension> <Assembly>NuGet.VisualStudio.Interop, Version=18.104.22.168, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly> <FullClassName>NuGet.VisualStudio.TemplateWizard</FullClassName> </WizardExtension>
NuGet.VisualStudio.Interop.dllis a new assembly that only contains the
TemplateWizardclass. This class is a simple wrapper that calls into the actual implementation that lives in
NuGet.VisualStudio.dll. The assembly version will never change so that project/item templates continue to work with new versions of NuGet.
Add the list of packages to install in the project:
<WizardData> <packages> <package id="jQuery" version="1.6.2" /> </packages> </WizardData>
The wizard supports multiple
<package>elements. Both the
versionattributes are required. An important consequence of this is that a specific version of a package will be installed even if a newer version is available in the online package feed.
The reason for this behavior is that a future version of a package might introduce a change that is not compatible with the project/item template. The choice to upgrade the package to the latest version using NuGet is left to the developer who is in the best position to assume the risks of upgrading the package to the latest version.
Starting with NuGet 2.2.1, the wizard also supports multiple
<packages>elements. This enables scenarios where some packages are installed from one repository, but other packages are installed from a different repository.
The remaining step is to specify the repository where NuGet can find the package files. As mentioned earlier, three package repository modes are supported:
VSIX package repository
The recommended approach for deploying Visual Studio project/item templates is through a VSIX package (read more about VSIX deployment here). The VSIX method is preferable because it allows you to package multiple project/item templates together and allows developers to easily discover your templates using the VS Extension Manager or the Visual Studio Gallery. On top of that you can easily push updates to your users using the Visual Studio Extension Manager automatic update mechanism.
To specify a VSIX as a package repository you modify the
<packages>element in the
<packages repository="extension" repositoryId="MyTemplateContainerExtensionId"> ... </packages>
repositoryattribute specifies the type of repository (“extension”) while
repositoryIdis the unique identifier of your VSIX (i.e. the value of the
IDattribute in the extension’s vsixmanifest file).
Add your nupkg files as custom extension content in your
source.extension.vsixmanifestfile. If you're using the 2.0 schema it should look like this:
<Asset Type="Moq.4.0.10827.nupkg" d:Source="File" Path="Packages\Moq.4.0.10827.nupkg" d:VsixSubPath="Packages" />
Or if you're using the 1.0 schema it should look like this:
<CustomExtension Type="Moq.4.0.10827.nupkg"> packages/Moq.4.0.10827.nupkg</CustomExtension>
Ensure that your
nupkgfiles are located under a folder called
Packageswithin the VSIX package.
You can place the nupkg files in the same VSIX as your project templates or you can have the packages be located in a separate VSIX if that makes more sense for your scenario (just note that you should not reference VSIXs you do not have control over since they could change in the future and your project/item templates would break).
Template package repository
If packaging multiple projects is not important to you (e.g. you are only distributing a single project/item template), a simpler but also more limited approach is to include the nupgk files in the project/item template zip file itself.
However, if you are bundling a set of project/item templates that relate to each other and share NuGet packages (e.g. you are shipping a custom MVC project template with versions for Razor, Web Forms, C#, and VB.NET), we do not recommend adding the NuGet packages directly to each project/item template zip file. It needlessly increases the size of the project/item template bundle.
- To specify the project/item template as a package repository you modify the
<packages repository="template"> ... </packages>
repositoryattribute now has the value "template" and the
repositoryIdattribute is not longer required. The nupkg files need to be placed into the root directory of the project/item template zip file.
Registry-specified folder path
Many SDKs are installed via MSI. These MSIs have the ability to install NuGet packages on disk for efficient package installation during project creation, avoiding the need to extract the packages during project creation. ASP.NET uses this approach for its preinstalled packages in project templates.
This approach requires a few moving parts:
- Put the packages on disk during MSI installation
- You can install only the nupkg files or the nupkg files along with their expanded contents.
- If the expanded contents are also installed, this saves one additional step during project creation.
- The file/folder format matches the standard packages folder used by NuGet where the nupkg files are all at the root and then each package has a subfolder with the id/version pair as the subfolder name.
- Write a registry key that can be used to reference the package installation folder
- Use HKEY_LOCAL_MACHINE\SOFTWARE[\Wow6432Node]\NuGet\Repository for the key location.
- The key name must be something unique to you. ASP.NET MVC 4 in VS 2012 uses "AspNetMvc4VS11" for instance.
- The value must be the full path to your packages folder. ASP.NET MVC 4 uses "C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET MVC 4\Packages\" for instance.
- Use the repository value of "registry" within the
- Specifying your registry key name in the
- If you have pre-unzipped your packages, use the
- Specifying your registry key name in the
Here's an example
<packages> element using the registry-specified folder repository:
<packages repository="registry" keyName="AspNetMvc4VS11" isPreunzipped="true"> <package id="EntityFramework" version="5.0.0" skipAssemblyReferences="true" /> ... </packages>
Note that the above example also uses the
skipAssemblyReferences="true" attribute, which is another performance
optimization. The VS template itself already includes this assembly reference, so we can tell NuGet to skip
adding assembly references from the package.
Make your VSIX declare a dependency on the NuGet VSIX by adding a reference to it in your VSIX manifest:
<Reference Id="NuPackToolsVsix.Microsoft.67e54e40-0ae3-42c5-a949-fddf5739e7a5" MinVersion="1.7.30402.9028"> <Name>NuGet Package Manager</Name> <MoreInfoUrl>http://docs.nuget.org/</MoreInfoUrl> </Reference> ....
Require project/item templates to be saved on creation by setting
<PromptForSaveOnCreation>in the .vstemplate file.
Packages in VS Templates sample project
A sample project is available to get you started. The source code is available here.