Saturday, January 31, 2015

Visual Studio Add-Ins

On my current project we have an n-tier solution that combines Angular JS, MVC for ASP.NET, and Web API.  When you want to create a "full stack" (all the way from the Angular files to the data access layer) you have to create 10 separate files.  Because of the naming conventions we use, we end up naming the files very similarly (e.g. SomeController, SomeBusiness, SomeDataAcess).  My boss tasked me with coming up with a way for the development team to create a full stack with as few steps as possible.  I ended up creating an add-in for Visual Studio that accepts a namespace and a root name ("Some" in the previous example) along with a few boolean options.  When you press the button, 10 custom templates are imported and modified to match the naming conventions.

This process will significantly decrease development time and coding errors.  Since we use Unity for IoC and DI, the add-in also registers each interface created with its respective concrete class.  We've effectively been able to eliminate the "class does not have a default constructor" error, which was a huge bonus.

It turns out creating an add-in is pretty easy, but getting it to do anything complex is a bit trickier.  Below are some pointers on creating an add-in so hopefully I don't make the same mistakes next time.


   1:  (Solution2)_applicationObject.Solution 

That little guy right there will get you the Solution (which seems a bit obvious now).


   1:  private IEnumerable<Project> GetProjects(List<string> projectNames)
   2:          {
   3:              var projects = new List<Project>();
   4:   
   5:              // get the projects from the root (solution) object
   6:              var solutionProjects = _applicationObject.Solution.Projects;
   7:   
   8:              // get the enumerator from the projects from the root (solution) object
   9:              var enumerator = solutionProjects.GetEnumerator();
  10:              // iterate the projects
  11:              while (enumerator.MoveNext())
  12:              {
  13:                  var project = (Project)enumerator.Current;
  14:   
  15:                  if (project == null)
  16:                  {
  17:                      continue;
  18:                  }
  19:   
  20:                  // check whether the project is a solution folder
  21:                  if (project.Kind == ProjectKinds.vsProjectKindSolutionFolder)
  22:                  {
  23:                      // if the project is a solution folder, get the projects from the solution folder
  24:                      projects.AddRange(GetSolutionFolderProjects(project, projectNames));
  25:                  }
  26:                  else if (projectNames.Contains(project.Name))
  27:                  {
  28:                      // if the project isn't a solution folder, add it to the results
  29:                      projects.Add(project);
  30:                  }
  31:              }
  32:   
  33:              return projects;
  34:          }

That beauty will get the projects at the root of the solution.  You'll notice on line 24 I'm calling another method named GetSolutionFolderProjects.  That's used in case your solution uses solution folder for easier visible navigation (like ours does).  Here's that method:


   1:  private IEnumerable<Project> GetSolutionFolderProjects(Project solutionFolder, List<string> projectNames)
   2:          {
   3:              var list = new List<Project>();
   4:   
   5:              // iterate the project items in the solution folder (each project item should either be another solution folder or a project
   6:              for (var i = 1; i <= solutionFolder.ProjectItems.Count; i++)
   7:              {
   8:                  var subProject = solutionFolder.ProjectItems.Item(i).SubProject;
   9:                  if (subProject == null)
  10:                  {
  11:                      continue;
  12:                  }
  13:   
  14:                  // check whether the project is a solution folder
  15:                  if (subProject.Kind == ProjectKinds.vsProjectKindSolutionFolder)
  16:                  {
  17:                      // if the project is a solution folder, get the projects from the solution folder
  18:                      list.AddRange(GetSolutionFolderProjects(subProject, projectNames));
  19:                  }
  20:                  else if (projectNames.Contains(subProject.Name))
  21:                  {
  22:                      // if the project isn't a solution folder, add it to the results
  23:                      list.Add(subProject);
  24:                  }
  25:              }
  26:   
  27:              return list;
  28:          }

You'll notice on line 18 I'm calling another method named GetSolutionFolderProjects... that's a little recursion joke.  If you didn't get it, remember this: in order to understand recursion, you must first understand recursion.

I'm going to post more about this later, but I've been meaning to write this post for over a month and just haven't made the time.  This is a start.

No comments:

Post a Comment