【问题标题】:T4 Toolbox - Referencing Class in Current AssemblyT4 Toolbox - 当前程序集中的引用类
【发布时间】:2010-07-21 10:08:23
【问题描述】:

我正在编写一个 T4 脚本,它反映某些类并提供基于它们的代码生成。问题是我的脚本出错了,说我当前项目中的类无法访问。

脚本本身与我试图引用的类位于同一个程序集中。我已经尝试引用命名空间、文件并添加对当前程序集(项目本身)的引用 - 都无济于事。

我错过了什么?

【问题讨论】:

    标签: .net visual-studio t4 t4-toolbox


    【解决方案1】:

    我相信这就是 Nicko 和 uosɐſ 正在寻找的东西。只需将“MyAssembly.CodeGeneration”更改为具有 T4 模板的项目名称即可。

    <#@ assembly name="$(TargetPath)MyAssembly.dll" #>
    <#@ import namespace="MyAssembly.CodeGeneration" #>
    

    【讨论】:

    • 我改用了 $(TargetPath)。它是 dll 的宏。
    • 如果你也离开原来的解决方案就好了
    【解决方案2】:

    要记住的一点是,您正在编写的 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。然后我们加载存储当前TemplateFileProject,并在FindClasses() 方法中解析出该项目中符合我们搜索条件的类。

    我希望示例代码为您提供一个起点 - 但如果您需要更多详细信息,这里有一些额外的参考资料供您学习:

    【讨论】:

      【解决方案3】:

      由于某种原因,我无法让@brian 解决方案发挥作用。我最终这样做了 就我而言,T4Generators 是我在同一解决方案中的单独项目。

      <#@ assembly name="$(SolutionDir)\T4Generators\bin\Debug\T4Generators.dll" #>
      <#@ import Namespace="T4Generators" #>
      

      【讨论】:

        【解决方案4】:

        引用它常用的方式。然后检查是否加载了程序集,如果没有 - 生成存根代码(使编译成为可能;编译后再次运行 T4,生成真实代码)。并进行单元测试以防止存根代码投入生产。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多