Search code examples
c#visual-studioenvdtetexttemplate

Skipping solution folders in TT automation script


I have a Text Template snippet which generates a Solution class with an Assemblies property to list all project assemblies in the Visual Studio solution to help with IoC container configuration (GitHub, NuGet). It fails when projects are placed in solution folders though. How to make solution folders transparent for this script? The problematic code is:

<#+

    class SolutionAssemblyNames : IEnumerable<string>
    {
        public SolutionAssemblyNames(ITextTemplatingEngineHost host)
        {
            Host = host;
        }

        public IEnumerator<string> GetEnumerator() => Assemblies.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        IEnumerable<string> Assemblies => Projects
            .Select(p => p?.Properties?.Item("AssemblyName")?.Value as string)
            .Distinct().Where(a => !string.IsNullOrWhiteSpace(a));

        IEnumerable<Project> Projects => VisualStudio.Solution.Projects.OfType<Project>();
        DTE VisualStudio => (Host as IServiceProvider).GetService(typeof(DTE)) as DTE;
        ITextTemplatingEngineHost Host { get; }
    }
#>

Solution

  • The DTE doesn't return a flat list of projects, so you need to proactively check for solution folders and then read its sub-projects, recursively (as you can have solution folders inside solution folders, etc.)

        IEnumerable<string> Assemblies => Projects
            .SelectMany(GetProjectAndSubProjects) // ################# Flatten the list of projects
            .Select(p => p?.Properties?.Item("AssemblyName")?.Value as string)
            .Distinct()
            .Where(a => !string.IsNullOrWhiteSpace(a));
    
        IEnumerable<Project> Projects => VisualStudio.Solution.Projects.OfType<Project>();
    
        private static IEnumerable<EnvDTE.Project> GetProjectAndSubProjects(EnvDTE.Project project)
        {
            if (project.Kind == VsProjectKindSolutionFolder)
            {
                return project.ProjectItems
                    .OfType<EnvDTE.ProjectItem>()
                    .Select(p => p.SubProject)
                    .Where(p => p != null)
                    .SelectMany(GetProjectAndSubProjects);
            }
    
            return new[] { project };
        }
    
        // Copied from EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder
        private const string VsProjectKindSolutionFolder = "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}";