Adding Items to Solution Template in GAX

I was thinking of adding a Test Project in each of the layer solution templates for my Layered Architecture Solution Guidance. So I created a layered web application and manually added a Test Project to study what sort of structure do I need to create.

It turns out that apart from adding the Test Project, Visual Studio 2010 also adds 2 files - local.testsettings and TraceAndTestImpact.testsettings into the solution for me. So off I went to modify my VSTemplate and to my greatest surprise, there are no easy way to add items into solutions. Yup! You can easily add items to projects but there are no (known documented) way to even add a Readme.txt to the solution template and posting to the MSDN Forums, gave me zero replies, no help, whatsoever. *Sad*

So I went on to think about it for a while. The VSTemplate does not allow an easy way to specify the files we want to add to a solution via its xml. We will need to perform some GAT and DTE acrobatics to achieve this. There are actually two problems. The first problem is how to get our file into the solution and the second problem is Visual Studio will always open the file automatically when we add it in. The second problem is not so much a problem if you are adding a readme.txt but becomes a problem when you are adding testsettings.

Let's look at the first problem. Because we can't specify the file in the VSTemplate, we can't package the testsettings file. We also can't point to a physical location of a testsettings file on the developer's machine to add. Therefore, the easiest workaround is to copy the contents of the testsettings file into a T4 template. From there we can generate the content out to a file on the developer's machine. To do this, we need to create an Action.

For the second problem, you will notice that Visual Studio will automatically prompts the developer to configure the test settings after your solution unfolds. This becomes very annoying and it took me a while to figure it out. To solve the problem, first add the testsettings file with an extension that Visual Studio does not recognize i.e. testsettings.bak, then save a copy of the file as the intended extension and delete the original. This prevents Visual Studio from launching the Test Setting dialog when you unfold your solution template.

Here's the Action I wrote (simplified for easy understanding):


[ServiceDependency(typeof(DTE))]
public class AddTestSettingAction : ConfigurableAction
{
    public override void Execute()
    {
        AddSettingsFile("local.testsettings"new TestSettingTemplate().TransformText());
        AddSettingsFile("TraceAndTestImpact.testsettings"new TraceTestImpactSettingTemplate().TransformText());
    }

    private void AddSettingsFile(string settingsFileName, string content)
    {
        DTE dte = GetService<DTE>(true);

        // Set the path to the physical solutions folder.
        string path = Path.GetDirectoryName((string)dte.Solution.Properties.Item("Path").Value) +
            @"\Tests\Solution Items";

        // Setup actual file name.
        string settingsFile = path + "\\" + settingsFileName);
        // Setup a temporary staging file name.
        string stagingFile = settingsFile + ".bak";

        // Write the contents to staging file.
        using (StreamWriter writer = new StreamWriter(stagingFile, false))
        {
            writer.WriteLine(content);
        }

        // Get the Tests\Solution Items folder.
        ProjectItem projectItem = dte.Solution.Projects.Item(dte.Solution.Projects.Count).ProjectItems.Item(1);

        // Add the staging file.
        projectItem.SubProject.ProjectItems.AddFromFile(stagingFile);

        // Return the item.
        ProjectItem item = projectItem.SubProject.ProjectItems.Item(projectItem.SubProject.ProjectItems.Count);

        // Create a new copy of the item with the desired file name.
        Window wnd = item.Open(EnvDTE.Constants.vsViewKindPrimary);
        item.SaveAs(settingsFile);
        wnd.Visible = false;
        wnd.Close();

        // Delete the staging file.
        File.Delete(stagingFile);
    }
}


Take note that I did some very dangerous assumption here based on the layout of my template. I created a Tests folder in my solution and it is always the last folder in sequence based on alphabetical order, therefore, I always went for the last index in my first level solution folder. The next dangerous thing I did was to always retrieve the 1st index for my sub-folder. I guessed if you have unpredictable layouts, you should write a search function for locating the project items, otherwise, you can hard-code them for performance.

And here's what has been achieved:


Now isn't that a lame workaround for a lame problem that should not have even existed at all in the first place?



No comments:

Post a Comment

Popular Post