Creating a custom Visual Studio project template for Ivonna

Yesterday I spent the whole day improving the project template for Ivonna, so I thought I might make this world a better place by sharing the details (and some code, too). The main idea is that, in order to make the template smarter than just being able to do some search/replace for you, you should go down the path of Visual Studio extensibility (and return alive). Other than that, you have to put your code in an assembly and throw it into the GAC,  then reference it in the vstemplate file. Ask Google for the details, they know.

What I'm trying to implement here: whenever a user adds a WebTest project to the solution, the template should do several things:

  1. Determine the project being tested, and add the test project's output to the bin folder of the tested Web. This is what the existing template does.
  2. Determine if the tested Web is an Asp.Net MVC project, and which version, and add the correct System.Web.MVC and Ivonna.Framework.MVC references to the test project. This is what the improved 3.0 template will do.

Finding a Web project in the solution

First, you have to list all projects in the solution, and check each one for its Web-ness. Doing the check is easy once you know all project's GUIDs -- you just compare them to the known Web GUIDs -- one for Web Site, the other for Web Application projects:

static string[] GetWebGuids() {
	return new[] {"{349C5851-65DF-11DA-9384-00065B846F21}", 
"{E24C65DC-7377-472B-9ABA-BC803B73C61A}"
}; }

Getting the project's GUID's is a tough challenge. No such property. Fortunately, this link has the code I wouldn't ever be able to figure on my own.

By the way, if you use project.Kind, you won't get it. For example, you'll get a "Windows C# Project" for a C# Web Application project.

Setting the project's output path

Setting the created Visual Studio project's output path from the project template is relatively easy:

private static void SetOutputPath(this Project project, string newPath) {
	var configuration = 
project.ConfigurationManager.ActiveConfiguration; configuration.Properties.Item("OutputPath").let_Value(newPath); }

Adding the required references to the project

The second task can be split into several steps:

  1. Get the project's references.
  2. Find the first, if any, reference to System.Web.MVC.
  3. Get its major version.
  4. Add the same reference to the project being created.
  5. Add the corresponding Ivonna.Framework.MVC reference.

Discovering the list of Visual Studio project references

A big surprise here: the EnvDTE.Project interface we're dealing with doesn't have a member for the references of the corresponding VS project. We'll have to use the VSLangProj80.VSProject2 interface instead:

public static IEnumerable<Reference> GetProjectReferences
(this Project project) { var vsProject = project.Object as VSProject2; return vsProject.References.Cast<Reference>(); }
The reference we're looking for should have its Name property equal to "System.Web.MVC". In order to add this reference to the new project, we cannot use its name, since it's the same for all MVC versions. Fortunately, we can use the full path here:
vsProject.References.Add(mvcReference.Path);
On the other hand, all Ivonna.Framework.MVC.* assemblies have different names; since they're discoverable by Visual Studio, we can use the correct name without the full path:
var version = reference.MajorVersion;
vsProject.References.Add("Ivonna.Framework.MVC." + version.ToString());
That's it. If you need the missing pieces, you'll find the code on GitHub.
blog comments powered by Disqus

Latest blog posts

Powered by FeedBurner

Archives