2010-07-21 45 views
12

我正在写一个T4脚本,它反映了某些类并提供了基于它们的代码生成。问题是我的脚本错误,说我的当前项目中的类不能被访问。T4 Toolbox - 在当前程序集中引用类

脚本本身驻留在相同的组件,我想引用的类。我已经尝试引用名称空间,文件并添加对当前程序集(项目本身)的引用 - 都无济于事。

我错过了什么?

回答

9

我相信这是力高和uosɐs正在寻找。只需使用T4模板将“MyAssembly.CodeGeneration”更改为项目名称即可。

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

改为使用$(TargetPath)。这是dll的宏。 <#@程序集名称=“$(TargetPath)”#> <#@ import namespace =“MyAssembly.CodeGeneration”#> – 2013-01-18 20:51:52

+0

如果您还保留原始解决方案 – 2017-03-25 01:15:46

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 。然后,我们加载在当前TemplateFile存储在Project,并在FindClasses()方法,该项目符合我们的搜索标准,其内解析出的类。

我希望示例代码为您提供了一个起点,从跳下 - 但如果你需要进一步的细节,这里有一些额外的参考,为您的牙齿沉入:

0

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

0

出于某种原因,我无法获得@brian解决方案的工作。我结束了这样做 在我的情况下,T4Generators是我在同一解决方案中的独立项目。

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