Read classes in a namespace in a t4 template
Asked Answered
E

3

10

is there a way I can read in a namespace and loop through all the classes in a t4 template using reflection or something?

<#foreach (class poco in LoadNamespace("Web.Code.Entities.Poco").Classes ) { #>
public interface I<# poco.ClassName #>Repository 
{
    IQueryable< <# poco.ClassName #> > Get();
    <# poco.ClassName #> Save(<# poco.ClassName #> entity);
    bool Delete(<# poco.ClassName #> entity);
}
<#} #>

example meta data:

namespace Web.Code.Entities.Poco
{
    public class Product
    { 
          public int Id { get; set; }
          public string Name{ get; set; }
          public string Category{ get; set; }
    }

    public class Employee
    { 
          public int Id { get; set; }
          public string Name{ get; set; } 
    }
}

desired output:

    public interface IProductRepository 
    {
        IQueryable<Product> Get();
        Product Save(Product entity);
        bool Delete(Product entity);
    }   

    public interface IEmployeeRepository 
    {
        IQueryable<Employee> Get();
        Employee Save(Employee entity);
        bool Delete(Employee entity);
    }
Ence answered 13/10, 2010 at 14:35 Comment(1)
dreamincode.net/code/snippet1539.htmEnce
B
13

First install T4Toolbox from here

Then In your Template ensure you have the following lines at top :

<#@ assembly name="EnvDTE" #> 
<#@ import namespace="EnvDTE" #>
<#@ include file="T4Toolbox.tt" #>

Then add these lines of codes:

<#+  

private List<CodeClass> FindClasses(string nameSpace, string className)
{
    List<CodeClass> result=new List<CodeClass>();
    FindClasses(TransformationContext.Project.CodeModel.CodeElements,className,nameSpace,result,false);
    return result;

}


private void FindClasses(CodeElements elements, string className,string searchNamespace,List<CodeClass> result,bool isNamespaceOk)
{
    if (elements==null)return;
    foreach (CodeElement element in elements)
    {       
        if(element is CodeNamespace)
        {
            CodeNamespace ns = element as CodeNamespace;
            if(ns != null)
            {
                if (ns.FullName == searchNamespace)
                    FindClasses(ns.Members, className,searchNamespace,result,true);
                else
                    FindClasses(ns.Members, className,searchNamespace,result,false);
            }
        }
        else if(element is CodeClass && isNamespaceOk)
        {
            CodeClass c = element as CodeClass;
            if (c != null)
            {
                if(c.FullName.Contains(className))
                    result.Add(c);

                FindClasses(c.Members, className,searchNamespace,result,true);
            }

        }
    }
}
#>

Then you can find all classes in a namespace by calling like this (the second parameter filters all classes which their names contain the passed string) :

FindClasses("NameSpace.SubNameSpace",""))//All classes in the specifed namespace 

OR

FindClasses("NameSpace.SubNameSpace","Repository")) //All classes in the specifed namespace which have "Repository" in their names

------------------------------------------ UPDATE FOR VS 2012 --------------------------

Use these functions and namespaces if your T4Toolbox is updated for VS 2012 :

//visual studio 2012+
<#@ assembly name="Microsoft.VisualStudio.Shell.11.0" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.Interop" #>
<#@ import namespace="Microsoft.VisualStudio.Shell" #>
<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>

private List<CodeClass> FindClasses(string nameSpace, string className,string baseClassName)
{
    List<CodeClass> result=new List<CodeClass>();
    FindClasses(GetProject().CodeModel.CodeElements,className,baseClassName,nameSpace,result,false);
    return result;

}


private void FindClasses(CodeElements elements, string className,string baseClassName,string searchNamespace,List<CodeClass> result,bool isNamespaceOk)
{
    if (elements==null)return;
    foreach (CodeElement element in elements)
    {       
        if(element is CodeNamespace)
        {
            CodeNamespace ns = element as CodeNamespace;
            if(ns != null)
            {
                if (ns.FullName == searchNamespace)
                    FindClasses(ns.Members, className,baseClassName,searchNamespace,result,true);
                else
                    FindClasses(ns.Members, className,baseClassName,searchNamespace,result,false);
            }
        }
        else if(element is CodeClass && isNamespaceOk)
        {
            CodeClass c = element as CodeClass;
            if (c != null)
            {
                if(c.FullName.Contains(className) && (baseClassName==null || (HasIt(c.Bases ,baseClassName) && c.Name != baseClassName)))
                    result.Add(c);

                FindClasses(c.Members, className,baseClassName,searchNamespace,result,true);
            }

        }
    }
}

private bool HasIt(CodeElements elements,string name)
{
    foreach (CodeElement element in elements)
    {
        if (element.Name==name)
            return true;
    }
    return false;
}

private Project GetProject()
{
    // Get DTE
    var dte = (DTE)TransformationContext.Current.GetService(typeof(DTE));

    // Get ProjectItem representing the template file
    ProjectItem projectItem = dte.Solution.FindProjectItem(TransformationContext.Current.Host.TemplateFile);

    // Get the Project of the template file
    Project project = projectItem.ContainingProject;

    return project;
}

private string GetDefaultNamespace()
{

    // Get DTE
    var dte = (DTE)TransformationContext.Current.GetService(typeof(DTE));

    // Get ProjectItem representing the template file
    ProjectItem projectItem = dte.Solution.FindProjectItem(TransformationContext.Current.Host.TemplateFile);

    // Get the Project of the template file
    Project project = projectItem.ContainingProject;

    var vsSolution = (IVsSolution)TransformationContext.Current.GetService(typeof(SVsSolution));
    IVsHierarchy vsHierarchy;
    ErrorHandler.ThrowOnFailure(vsSolution.GetProjectOfUniqueName(project.FullName, out vsHierarchy));
    uint projectItemId;
    ErrorHandler.ThrowOnFailure(vsHierarchy.ParseCanonicalName(projectItem.FileNames[1], out projectItemId));
    object defaultNamespace;
    ErrorHandler.ThrowOnFailure(vsHierarchy.GetProperty(projectItemId, (int)VsHierarchyPropID.DefaultNamespace, out defaultNamespace));
    return ((string)defaultNamespace);
}

so your search will be something like this :

FindClasses(GetDefaultNamespace(),"Repository","RepositoryBase")
Boccie answered 26/1, 2012 at 22:32 Comment(6)
Note: In VS2012 TransformationContext has changed: olegsych.com/2012/12/t4-toolbox-for-visual-studio-2012Annalist
I'm using VS2012 with your updated code and included EnvDTE.dll in my project, but it's saying IVsSolution, SVsSolution, IVsHierarchy can not be found within the .tt file. Any ideas?Epaulet
Found a fix, just added it to the answer.Epaulet
Jimmy I didnt see your fix but I added it myself. It was from 2 missing referencesCredits
Thanks for your suggestion, I correct the post to feedback the recommended changes.Boccie
I kept getting “A template containing a class feature must end with a class feature” or "A namespace cannot directly contain members such as fields or methods" errors. Long story short: put this code block (contained by <#+ #> AFTER the final generated statement, in my case the closing brace for the namespace.Bois
C
2

Your best bet is to use the Visual Studio CodeModel, which is part of the DTE automation APIs. You can get hold of the DTE by making your template HostSpecific, casting your Host property to IServiceProvider and then doing a GetService() call.

There's an example of doing this by Colin Eberhardt on CodeProject: http://www.codeproject.com/Articles/39071/Declarative-Dependency-Property-Definition-with-T4.aspx

Cheapen answered 8/2, 2011 at 1:39 Comment(0)
L
1

I am using T4 in a .net core project,

My T4 is fairly large, so just extracting the relevant info here.. [In my case I am looking for Entity Framework Models and I know these are the only classes in the namespace, you may need to filter out your own]

referencing these nuget packages:

<#@ assembly name="$(UserProfile)\.nuget\packages\Microsoft.VisualStudio.TextTemplating.Interfaces.14.0\14.3.25407\lib\net45\Microsoft.VisualStudio.TextTemplating.Interfaces.14.0.dll" #>
<#@ assembly name="$(UserProfile)\.nuget\packages\Microsoft.VisualStudio.TextTemplating.14.0\14.3.25407\lib\net45\Microsoft.VisualStudio.TextTemplating.14.0.dll" #>

these imports/includes:

<#@ import namespace="EnvDTE" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating.Interfaces" #>
<#@ include file="$(UserProfile)\.nuget\packages\T4.VsAutomationHelper\1.0.0\tools\ttinc\VsAutomationHelper.CS.ttinclude" #>
<#@ include file="$(UserProfile)\.nuget\packages\T4.TemplateFileManager\2.2.1\tools\ttinc\TemplateFilemanager.CS.ttinclude" #>

I have this method:

// Get all CodeClass Items in specified namespace
    public List<EnvDTE.CodeClass> GetClassesInNameSpace(IEnumerable<ProjectItem> items, string nameSpace)
    {
        var classItems = new List<EnvDTE.CodeClass>();

        var csFileProjectItems = items.Where(d => d.Properties.Item("FullPath").Value.ToString().EndsWith(".cs"));

        foreach(ProjectItem csFileProjectItem in csFileProjectItems)
        {
            EnvDTE.FileCodeModel model = csFileProjectItem.FileCodeModel;
            foreach(var modelCodeElements in model.CodeElements)
            {
                if (modelCodeElements is EnvDTE.CodeNamespace)
                {
                    var codeNameSpace = ((EnvDTE.CodeNamespace)modelCodeElements);

                    if (codeNameSpace.FullName == nameSpace)
                    {
                        foreach(var modelCodeElementChild in codeNameSpace.Children)
                        {
                            if (modelCodeElementChild is EnvDTE.CodeClass)
                            {
                                classItems.Add((EnvDTE.CodeClass)modelCodeElementChild);
                            }
                        }
                    }
                }
            }
        }
        return classItems;
    }

and call it using: (replace "App.Data.Model.Entities" with your own)

    var projectItems = this.dteHelper.GetAllProjectItems();
        var entityClasses = GetClassesInNameSpace(projectItems, "App.Data.Model.Entities");

    foreach (var entityClass in entityClasses)
    {
        // entityClass.Name

        // Important for .NetCore
        // when calling fileManager.StartNewFile() specify all the parameters, its didn't work for me otherwise eg:
        // code appreciated (file manager created before following code)

        var fileProperties = new FileProperties()
{ 
    BuildAction = BuildAction.Compile
};
        fileManager.StartNewFile(dataAccessFileName, generatedFilesTargetProject, generatedFilesTargetFolder, fileProperties);
    fileManager.IsAutoIndentEnabled = true;
    fileManager.CanOverwriteExistingFile = true;
    }
Laurenelaurens answered 3/9, 2016 at 6:10 Comment(1)
Where dteHelper comes from?Ferriferous

© 2022 - 2024 — McMap. All rights reserved.