ASP.NET MVC Tip #16 – Create ASP.NET MVC Macros

In this tip, I show you how you can create a Visual Studio 2008 macro that creates a new MVC controller, view folder, and controller unit test with a single command.

Don’t get me wrong. I like the Visual Studio 2008 designer tools. I like dragging-and-dropping items from the toolbox. I’ve memorized many useful Visual Studio keyboard shortcuts. But, at the end of the day, there is nothing faster than firing off a quick command from the Command window.

In this tip, I explain how you can take advantage of Visual Studio macros and the Visual Studio Command window to generate files and code for an ASP.NET MVC project. In particular, I explain how you can create a macro that creates a new MVC controller, MVC view, and MVC controller unit test.

Creating Visual Studio Macros

There are two approaches that you can take to creating new Visual Studio macros. First, you can record your actions when interacting with Visual Studio. To do this, select the menu option Tools, Macros, Record TemporaryMacro. A VCR-like control panel appears. After you are finished recording, you can click the Stop button and you will have a new macro that you can save and replay in the future.

The other approach is to write the macro from scratch. I took this second approach when creating my MVC macros. I was forced to use this second approach since I needed to write generic functions that would work with any MVC project.

Visual Studio macros must be written in the Visual Basic .NET programming language. The majority of your macro code consists of calls to the Visual Studio Automation Object Model. My MVC macros took advantage of the following Automation objects:

· DTE – The top level object in the Visual Studio Automation Model. This object has a Solution property that represents your Visual Studio Solution. You can use the Solution property to get to the individual projects in your Solution.

· Project – Represents an individual project in your solution such as an MVC project or a Test project. Use the ProjectItems property to get to individual project items such as project files and folders.

· CodeModel – Represents the code in a project. You can use the CodeModel to add new classes to a project.

You create a new Visual Studio Macro Project by selecting the menu option Tools, Macros, New Macro Project. Selecting this menu option opens the New Macro Project Dialog Box (see Figure 1).

Figure 1 – New Macro Project Dialog Box

clip_image002

After you create a new Macro Project, the Macro Explorer window opens in the location normally occupied by the Solution Explorer window (see Figure 2). You can double-click any macro in the Macro Explorer window to run a macro. You also can right-click a macro in the Macro Explorer and select the menu option Edit to modify the macro.

Figure 2 – Macro Explorer Window

clip_image004

When you edit a macro, the Macro IDE appears. The Macro IDE looks just like another instance of Visual Studio 2008. However, it is a more limited development environment that is specifically designed for developing macros.

You create a set of macros by creating a Visual Basic .NET module. Each public subroutine that you define in the module is exposed as a separate macro. For example, the code in Listing 1 a super simple macro that simply displays a message box with the message “Hello World!”.

Listing 1 – Test.vb

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics
 
Public Module Test
 
    Sub SayHello()
        MsgBox("Hello World!")
    End Sub
 
End Module

A macro subroutine can accept parameters. However, all parameters must be Optional parameters – use the Visual Basic Optional keyword. If you don’t make the parameters optional, then the subroutine won’t appear in the Macro Explorer window.

Executing Visual Studio Macros

After you create a macro, there are several ways to execute it:

· From the Macro Explorer window

· From the Command window

· From the Find input box

The easiest way to execute a macro is to double-click the macro in the Macro Explorer window. However, that defeats the purpose of this tip. The goal of this tip is to explain how you can quickly modify an ASP.NET MVC project by typing out commands.

The second option is to execute a macro from the Command window. You can open the Command window by selecting the menu option View, Other Windows, Command Window (or better, use the keyboard combination Ctl-Alt-A). After the Command window opens, you can fire off a macro by typing Macros.Macro Name. For example, you can execute the macro that we created in the previous section by typing the following command into the Command window:

Macros.MVC.Test.SayHello

You get full statement completion when entering a macro name (see Figure 3). So you really only need to enter the letter “m” and arrow down to the macro that you want to execute.

Figure 3 – Command window statement completion

clip_image006

I discovered this last method of executing a macro from Sara Ford’s blog at:

http://blogs.msdn.com/SaraFord/

You can execute a macro from the Find input box that appears in the Visual Studio toolbar (see Figure 4). Type a “>” followed by the name of the macro to execute the macro. You can navigate quickly to the Find input box from the keyboard by using the keyboard combination Ctl+/ (Press the Ctl key and press the forward slash).

Figure 4 – Executing a macro from the Find input box

clip_image008

Creating Macro Aliases

If you really want to reduce the amount of typing required to execute a macro then you can create a macro alias. Just use the alias command like this:

alias h Macros.MVC.Test.SayHello

After you execute this command from the Command window, you can execute the SayHello macro simply by typing the single letter h.

You delete an alias with the /delete switch like this:

Alias h /delete

By taking advantage of aliases, you can perform common ASP.NET MVC tasks with the bare minimum of typing.

Creating an ASP.NET MVC Macro

The MVC macros are contained in Listing 2. The file in Listing 2 contains a module named Generate that contains four public subroutines named All, Controller, View, and ControllerTest.

Listing 2 – Generate.vb

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics
Imports System.IO
 
Public Module Generate
 
    Sub All(Optional ByVal name As String = "NewThing")
        ' Generate Controller
        Controller(name)
 
        ' Generate View
        View(name)
 
        ' Generate Controller Test
        ControllerTest(name)
    End Sub
 
    Sub Controller(Optional ByVal name As String = "NewThing")
        ' Get MVC project
        Dim mvcProj = GetMVCProject()
        If Not IsNothing(mvcProj) Then
            BuildControllerClass(mvcProj, name)
        End If
    End Sub
 
    Sub View(Optional ByVal name As String = "NewThing")
        ' Get MVC project
        Dim mvcProj = GetMVCProject()
        If Not IsNothing(mvcProj) Then
            BuildView(mvcProj, name)
        End If
    End Sub
 
    Sub ControllerTest(Optional ByVal name As String = "NewThing")
        ' Get Test project
        Dim testProj = GetTestProject()
        If Not IsNothing(testProj) Then
            BuildControllerTestClass(testProj, name)
        End If
    End Sub
 
    Private Sub BuildControllerClass(ByVal project As Project, ByVal name As String)
        Dim controllerName As String = name & "Controller.cs"
        Dim controllerPath = Path.Combine(Path.GetDirectoryName(project.FileName), Path.Combine("Controllers", controllerName))
        Dim newController As CodeClass = project.CodeModel.AddClass(Path.GetFileNameWithoutExtension(controllerName), controllerPath, 0, "System.Web.Mvc.Controller", Nothing, vsCMAccess.vsCMAccessPublic)
 
        ' Add Index Function
        Dim func As CodeFunction2 = newController.AddFunction("Index", vsCMFunction.vsCMFunctionFunction, "System.Web.Mvc.ActionResult")
        func.Access = vsCMAccess.vsCMAccessPublic
    End Sub
 
    Private Sub BuildView(ByVal project As Project, ByVal name As String)
        Dim viewsFolder As ProjectItem = GetViewsFolder(project)
        If Not IsNothing(viewsFolder) Then
            viewsFolder.ProjectItems.AddFolder(name)
        End If
    End Sub
 
    Private Sub BuildControllerTestClass(ByVal project As Project, ByVal name As String)
        Dim controllerName As String = name & "ControllerTests.cs"
        Dim controllerPath = Path.Combine(Path.GetDirectoryName(project.FileName), Path.Combine("Controllers", controllerName))
        Dim newControllerTest As CodeClass = project.CodeModel.AddClass(Path.GetFileNameWithoutExtension(controllerName), controllerPath, 0, Nothing, Nothing, vsCMAccess.vsCMAccessPublic)
        newControllerTest.AddAttribute("Microsoft.VisualStudio.TestTools.UnitTesting.TestClass", String.Empty)
 
        ' Add Test Function
        Dim func As CodeFunction2 = newControllerTest.AddFunction("IndexTest", vsCMFunction.vsCMFunctionSub, "void")
        func.AddAttribute("Microsoft.VisualStudio.TestTools.UnitTesting.TestMethod", String.Empty)
        func.Access = vsCMAccess.vsCMAccessPublic
    End Sub
 
    ' Gets MVC project based on NO Tests suffix
    Private Function GetMVCProject() As Project
        For Each proj As Project In DTE.Solution.Projects
            If Not proj.Name.EndsWith("tests", StringComparison.InvariantCultureIgnoreCase) Then
                Return proj
            End If
        Next
        Return Nothing
    End Function
 
    ' Gets test project based on Tests suffix
    Private Function GetTestProject() As EnvDTE.Project
        For Each proj As EnvDTE.Project In DTE.Solution.Projects
            If proj.Name.EndsWith("tests", StringComparison.InvariantCultureIgnoreCase) Then
                Return proj
            End If
        Next
        Return Nothing
    End Function
 
    Private Function GetViewsFolder(ByVal project As Project)
        For Each item As ProjectItem In project.ProjectItems
            If String.Compare(item.Name, "Views") = 0 Then
                Return item
            End If
        Next
        Return Nothing
    End Function
End Module
 

You can create a new controller, view folder, and controller test by executing the following command from either the Command window or the Find input box:

Macros.MVC.Generate.All Product

Executing this command creates a new controller named ProductController, a new view folder named Product, and a new unit test named ProductControllerTests.

If you want to reduce the amount of typing that you need to execute these commands, then you can create a macro alias like this:

alias gen Macros.MVC.Generate.All

This command creates an alias named gen that represents the MVC.Generate.All macro. After you create the alias, you can create a new controller, view folder, and unit test class like this:

gen Customer

This is the ultimate approach to creating MVC project items in the fastest and laziest possible way! After executing this command, your Solution will contain the files in Figure 5.

Figure 5 -- Generating a Customer

image

 

Summary

My goal with this tip was to show that it is possible to build commands that can be executed from within the Visual Studio IDE that enable you to quickly generate ASP.NET MVC project items. By taking full advantage of the powerful Automation Model included in Visual Studio 2008, you could (theoretically) build entire ASP.NET MVC Applications without opening a single dialog box or selecting a single menu item.

4 Comments

  • This is something I think should be included in OOTB template.
    I have some remarks:
    1. In the generated controller class, there are no 'using' statements, as well as the namespace declaration - there are only class declaration and action result method. How do we customize the code created?
    2. Also in test controller class, situation is the same
    I tried to record a macro to see how this can be done, but I only have DTE Commands with string arguments

  • @panjkov -- You are right that the code included in this tip is really more of a Proof of Concept code sample than anything else. I wanted to show that you could do code generation easily from the Visual Studio Command window. I think it would be really useful and cool to create macros that did more extensive code generation (like create views, LINQ to SQL entities, and so on).

  • This is a pretty useful tool. It needs a little tweaking, but I like this better than my NAnt solution I did recently.

    I'm concerned about the method of selecting the project, as it seems pretty brittle. Most of my apps have many projects, and only one of them is the web project.

    Anyway, cool stuff!

  • Asp net mvc tip 16 create asp net mvc macros.. Tiptop :)

Comments have been disabled for this content.