T4工具箱 - 引用当前程序集中的类

我正在编写一个T4脚本,它反映了某些类,并提供基于它们的代码生成。问题是我的脚本错误,说我当前项目中的类无法访问。 脚本本身与我尝试引用的类位于同一个程序集中。我已经尝试引用命名空间,文件并添加对当前程序集(项目本身)的引用 - 一切都无济于事。 我错过了什么?     
已邀请:
我相信这就是Nicko和uos所寻求的。只需使用T4模板将“MyAssembly.CodeGeneration”更改为项目名称即可。
<#@ assembly name="$(TargetPath)MyAssembly.dll" #>
<#@ import namespace="MyAssembly.CodeGeneration" #>
    
要记住的一件事是,您正在编写的T4脚本实际上并不“驻留在同一个程序集中” - 因为它是设计时代码,而不是运行时代码。换句话说 - 它根本不会被编译到你的程序集中。 相反,在编写代码时运行T4模板脚本 - 或者,如果启用了某些挂钩,则无论何时构建/编译程序。因为它实际上与您的项目,代码分开,所以它无法直接引用您的项目程序集 - 除非您使用类似DTE的东西 - 这使您能够访问Visual Studio环境本身,并探索当前的元素已加载的项目。 例如,请考虑以下脚本:
<#@ template language="C#" debug="false" hostspecific="true" #>
<#@ output extension=".js" #>
<#@ assembly name="System" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data.Entity" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
<#@ include file="T4Toolbox.tt" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Reflection" #>

string targetNamespace = "MyNamespace";
var dte = (DTE)TransformationContext.Current.GetService(typeof(DTE));
var project = dte.Solution.FindProjectItem(TransformationContext.Current.Host.TemplateFile).ContainingProject);

var classes = FindClasses(project, targetNamespace, "");

<# foreach (CodeClass c in classes) { #>

    public class <#= c.Name #> {

<#     var properties = c.Members.OfType<EnvDTE.CodeProperty>()
           .Where(p => p.Access.HasFlag(vsCMAccess.vsCMAccessPublic))
           .OrderBy(p => p.Name);
       foreach (var prop in properties) { 
#>

       public <#= prop.Type.AsString #> <#= prop.Name #> { get; set; }

<#     } #>

   }

<# } #>

<#+ List<CodeClass> FindClasses(Project project, string ns, string className) {
        List<CodeClass> result = new List<CodeClass>();
        FindClasses(project.CodeModel.CodeElements, className, ns, result, false);
        return result;
    }
    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);
                }
            }
        }
    }
从本质上讲,这个脚本将运行一个特定的命名空间(在本例中为
"MyNamespace"
),遍历其中的所有类,然后输出一个新的代码文件,该文件仅用
getter
/
setter
列出它们的公共属性 - 实质上,生成一个POCO的对象。在我的一些项目中,我使用这个代码的改编版本来生成基于我的POCO的JavaScript对象,这样我的JS模型总是可以与我的服务器端对象同步,从序列化的角度来看。 但是,它的诀窍在于前几行:
var dte = (DTE)TransformationContext.Current.GetService(typeof(DTE));
var project = dte.Solution.FindProjectItem(TransformationContext.Current.Host.TemplateFile).ContainingProject);
var classes = FindClasses(project, targetNamespace, "");
从本质上讲,DTE服务要求Visual Studio为它提供当前加载的
Solution
的抽象模型,它是
Projects
。然后我们加载存储当前
TemplateFile
Project
,并在
FindClasses()
方法中解析该项目中符合我们搜索条件的类。 我希望示例代码为您提供一个起点 - 但如果您需要进一步的细节,这里有一些额外的参考资料可供您深入了解: DTE和T4:更好地结合在一起 从T4文本模板访问Visual Studio 在C#T4模板中通过DTE访问项目     
参考它常见的方式。然后检查是否加载了程序集 - 如果没有 - 生成存根代码(使编译成为可能;在编译之后再次运行T4,以生成实际代码)。并且有单元测试可以防止存根代码进入生产。     
出于某种原因,我无法让@brian解决方案正常工作。我最终这样做了 就我而言,T4Generators是我在同一解决方案中的独立项目。
<#@ assembly name="$(SolutionDir)T4GeneratorsbinDebugT4Generators.dll" #>
<#@ import Namespace="T4Generators" #>
    

要回复问题请先登录注册