I have amassed a lot of code. When I’m creating a new web application, this is handy because it provides me with a good basis. However, I’ve never gone to the trouble of wrapping all of this into a template to eliminate the redundancy.
My typical web application (solution) has a few projects like this to provide various functions for my solution:
- Web App
- Domain Layer
- Data Access Layer
- Common Utilities
- Web App Unit Tests
- Domain Unit Tests
So much of the code in these projects over the past two years has become increasingly reusable and shared:
- Angular directives, services, interceptors
- HTML layouts
- CSS Styles
- Repository interfaces, concrete implementations based on generics, Entity Framework common implementations
- Utilities and extension methods for building expressions, paging data, and lots of other common functionality
As I create a new project, I have found that more often than not, I was copy/pasting code with really need nothing more than a bit of renaming.
This redundancy had to go. It had become so tedious.
The first step that I took to eliminate this redundancy was to take my utilities and data access projects, give them some uniform namespacing, and turn them into Nuget packages. I then created a “web content” Nuget package that encapsulated all of the common Angular scripts that I have, HTML layouts, CSS styles, etc. There are also some common C#/.NET (MVC and WebApi) functionality that I found I could extract into a fourth Nuget project. These classes/functionality included web.config transforms for configuring the OWIN handler I use, filters (like for Elmah), attributes, common ViewModels, static methods, Html helper extensions, common Bundle configs, and various reusable configuration code.
With all of these Nuget packages in place, I was only halfway there. I would still have to create, from scratch using built-in VS templates, my new Mvc/WebApi solutiion with supporting projects, import all of these Nuget packges, and still go through the process of wiring up the boiler plate stuff. If you’ve ever set up a Mvc/WebApi app from scratch, you know this pain. It involves lots of importing of 3rd party Nuget packages (Elmah, Ninject, OWIN, etc), and configuring them. Just as an example to eliminate all of this, one of my Nuget packages now wrapped all of my OWIN (IAppBuilder) configuration, bundles, etc and exposed them as single methods. It’s actually quite nice. As an example, in the web project where I’m importing these Nuget packges, my configurations simply execute methods from the packages:
public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { Core.Web.AppStart.BundleConfig.RegisterBundles(bundles); } } public static class GlobalConfig { public static void CustomizeConfig(HttpConfiguration config) { Core.Web.AppStart.GlobalConfig.CustomizeConfig(config); } } public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { Core.Web.AppStart.FilterConfig.RegisterGlobalFilters(filters); } }
That’s pretty nice, if I do say so myself. However, there are some things, like Ninject configurations, which are harder to pull into a project with running PowerShell scripts to rename/modify the files to import, etc. And then I started thinking – why should I have to even create that? This is where Visual Studio templating comes in. The template can give me all of the base import Nuget references, solution structure, proper renaming, and ALL of the base functionality that I want without having to do anything. The end goal is to be able to have a template that can create a fully working solution that results in displaying a styled web page with EVERYTHING, like security, all wired up right from the get-go when you hit F5.
My starting point was to go through the process (hopefully for one last time) of creating my Angular App with all references, project strucutre, and the like from scratch:
Visual Studio provides built-in capabilities to Export projects as templates. But, the only thing supported (for free) is exporting a single project as a template ZIP file. Out of the box, this doesn’t do much for multi-project solutions.
However, we can export each project individual. Visual Studio does a few nice things for us like replacing certain elements of each project with tokens that can be replaced later on based on user input when creating a new project from the template.
Once each project is exported, we can extract each ZIP to its own folder, and create our own “vstemplate” referencing each of those templates. If we create a ZIP of that folder containing our VSTemplate file and each project in its own subfolder, dropping the ZIP into Visual Studio 2015’s Template’s folder (..\Documents\Visual Studio 2015\Templates\ProjectTemplates\Visual C#) works nicely. We can also take advantage of Visual Studio’s tokens to handle sub-project renaming. The most obvious token is $safeprojectname$ which is based on the user’s naming of the project. An example working VSTemplate file is below. You can see how I used each project is getting renamed with the ProjectName attribute and $safeprojectname$ token.
<VSTemplate Version="3.0.0" Type="ProjectGroup" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005"> <TemplateData> <Name>AngularApp</Name> <Description>An example of a multi-project template</Description> <ProjectType>CSharp</ProjectType> <Icon>AngularAppSources.ico</Icon> <ProjectSubType> </ProjectSubType> <SortOrder>1000</SortOrder> <CreateNewFolder>false</CreateNewFolder> <DefaultName>MyAngularApp</DefaultName> <ProvideDefaultName>true</ProvideDefaultName> <LocationField>Enabled</LocationField> <EnableLocationBrowseButton>true</EnableLocationBrowseButton> </TemplateData> <TemplateContent> <ProjectCollection> <ProjectTemplateLink ProjectName="$safeprojectname$"> AngularApp.Web\MyTemplate.vstemplate </ProjectTemplateLink> <ProjectTemplateLink ProjectName="$safeprojectname$.Tests"> AngularApp.Web.Tests\MyTemplate.vstemplate </ProjectTemplateLink> <ProjectTemplateLink ProjectName="$safeprojectname$.Domain"> AngularApp.Domain\MyTemplate.vstemplate </ProjectTemplateLink> <ProjectTemplateLink ProjectName="$safeprojectname$.Domain.Tests"> AngularApp.Domain.Tests\MyTemplate.vstemplate </ProjectTemplateLink> <ProjectTemplateLink ProjectName="$safeprojectname$.DataAccess"> AngularApp.DataAccess\MyTemplate.vstemplate </ProjectTemplateLink> </ProjectCollection> </TemplateContent> </VSTemplate>
This all works. Initially, I thought my job was done. But, there are a few issues with this approach. One minor issue is that we’re left with a ZIP that anyone wanting to use the template would have to drop into their Templates folder to utilitize. This is less than desirable. Another issue is that you can’t create solution items / folders with a VSTemplate. I was lead to investigating how to create a Visual Studio Extension installer (VSXI) to eliminate passing around a ZIP file.
There was one major problem with this approach. I always wound up with Visual Studio creating an extra folder. It would be between the projects and the solution like “\Solution\Solution\Project1.” This actually breaks Nuget because packages are put into the solution folder and then the hintpath, and DLL references, are borked. The directory structure really has to be “\Solution\Project1” (etc) for things to work properly.
In my next post, I’ll show how to eliminate these problems. These problems are overcome by using the Visual Studio SDK’s automation tools and wrapping our Template(s) into a Visual Studio Extension and ProjectItem solution.
Hey, are you planning on doing part 2? It would help. Thank you!
I really had thought that I did create a part 2. It looks like I didn’t start a draft, though. I’ll refresh my memory and work on a new post soon(ish).