有没有一种方法可以在命名空间中读取并使用反射或其他东西循环遍历t4模板中的所有类?
<#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);
}
<#} #>
示例元数据:
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; }
}
}
期望的输出:
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);
}
答案 0 :(得分:13)
然后在您的模板中确保您在顶部有以下行:
<#@ assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
<#@ include file="T4Toolbox.tt" #>
然后添加以下代码行:
<#+
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);
}
}
}
}
#>
然后你可以通过这样调用找到命名空间中的所有类(第二个参数过滤所有名称包含传递字符串的类):
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
------------------------------------------更新VS 2012 --------------------------
如果为VS 2012更新了T4Toolbox,请使用这些函数和命名空间:
//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);
}
所以你的搜索将是这样的:
FindClasses(GetDefaultNamespace(),"Repository","RepositoryBase")
答案 1 :(得分:2)
您最好的选择是使用Visual Studio CodeModel,它是DTE自动化API的一部分。 你可以通过制作模板HostSpecific来获取DTE,将你的Host属性转换为IServiceProvider,然后进行GetService()调用。
Colin Eberhardt就CodeProject做了一个例子: http://www.codeproject.com/Articles/39071/Declarative-Dependency-Property-Definition-with-T4.aspx
答案 2 :(得分:1)
我在.net核心项目中使用T4,
我的T4相当大,所以只需在这里提取相关信息.. [在我的情况下,我正在寻找实体框架模型,我知道这些是命名空间中唯一的类,您可能需要过滤掉自己的类]
引用这些nuget包:
<#@ 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" #>
这些进口/包括:
<#@ 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" #>
我有这个方法:
// 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;
}
并使用以下方法调用它:(将“App.Data.Model.Entities”替换为您自己的)
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;
}
答案 3 :(得分:0)
@Mahmoud Morave提供了很好的方法,但不幸的是,它并不适用于Core项目。我不得不创建一个单独的.NET项目。