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
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
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
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
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
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.