Monday, December 13, 2010

Using macros in Visual Studio to rename files and classes

Recently I was working on a project that was initially started back in 2005. The solution is divided in several layers: entity layer, business layer, data layer and UI. For the first three layers CodeSmith is used to generate base classes based on the database schema. Extra functionality is added by implementing derived classes. Every database change or new common functionality can be added for all classes by changing a CodeSmith template. Very handy if there are more than 100 objects per layer.

There was one drawback, however. The templates generated the files in the several layers with the same name. So a table named “Pet”, would create three “Pet” classes: one in the entity layer, one in the business layer and one in the data layer. On top of that, the derived classes had the same filename, “Pet.cs” (located in a different directory). So when coding or debugging, all tabs in Visual Studio had the same label “Pet.cs”, very confusing! My objective was to change the filenames and classes in such a way that the class renames are refactored in the whole solution and that the file renames are reflected in MS Visual Source Safe. My solution: a macro!

So go to “Tools > Macros > New macro project” to create a macro project. Macros are written in VB. In the macro project I created the several methods, one for each layer and some common methods. It’s pretty self explanatory. The RenameSymbol() method causes the rename of the class to be reflected in all of the code!

Sub RenameBussinessLayer()
  For Each project As Project In DTE.Solution.Projects
    If project.Name = "BusinessLogic" Then
      For Each item As ProjectItem In project.ProjectItems
        If item.Name = "Generated" Or item.Name = "Implementation" Then
          RenameBLClasses(item.ProjectItems)
        End If
      Next
    End If
  Next
End Sub
Sub RenameBLClasses(ByVal items As ProjectItems)
  For Each item As ProjectItem In items
    item.Open()

    Dim doc As Document
    doc = item.Document
    If doc.Path.Contains("BusinessLogic") Then
      RenameClass(doc, "BL")
    End If
  Next
End Sub
Sub RenameClass(ByVal doc As Document, ByVal layer As String)
    Dim element As CodeElement
    Dim newName As String
    Dim postFix As String

    element = GetClassElement(doc.ProjectItem.FileCodeModel.CodeElements)

    If layer = "DAL" Then
        postFix = "Repository"
        newName = element.Name & postFix
    ElseIf layer = "BL" Then
        postFix = "Manager"

        ' class name is already OK, only filename needs to change
        newName = element.Name
    ElseIf layer = "OM" Then
        postFix = element.Name

        ' class name is already OK, only filename needs to change
        newName = element.Name
    End If

    ' Rename file
    If Not doc.ProjectItem.Name.EndsWith(postFix & ".cs") Then
        doc.ProjectItem.Name = newName & ".cs"
    End If

    ' Rename class
    If Not element.Name.EndsWith(postFix) Then
        Dim elementToRename As CodeElement2
        elementToRename = DirectCast(element, CodeElement2)
        elementToRename.RenameSymbol(newName)
    End If
End Sub
Private Function GetClassElement(ByVal elements As CodeElements) As CodeElement
    For Each element As CodeElement In elements
        If element.Kind = vsCMElement.vsCMElementImportStmt Then
            Continue For
        ElseIf element.Kind = vsCMElement.vsCMElementClass Then
            Return element
        Else
            Return GetClassElement(GetElementMembers(element))
        End If
    Next

    Return Nothing
End Function
Private Function GetElementMembers(ByVal element As CodeElement) As CodeElements
    Dim elements As EnvDTE.CodeElements

    If TypeOf element Is EnvDTE.CodeNamespace Then
        elements = CType(element, EnvDTE.CodeNamespace).Members
    ElseIf TypeOf element Is EnvDTE.CodeType Then
        elements = CType(element, EnvDTE.CodeType).Members
    ElseIf TypeOf element Is EnvDTE.CodeFunction Then
        elements = DirectCast(element, EnvDTE.CodeFunction).Parameters
    End If

    Return elements
End Function

1 comment:

  1. This comment has been removed by a blog administrator.

    ReplyDelete