我正在写一个T4脚本,它反映了某些类并提供了基于它们的代码生成。问题是我的脚本错误,说我的当前项目中的类不能被访问。T4 Toolbox - 在当前程序集中引用类
脚本本身驻留在相同的组件,我想引用的类。我已经尝试引用名称空间,文件并添加对当前程序集(项目本身)的引用 - 都无济于事。
我错过了什么?
我正在写一个T4脚本,它反映了某些类并提供了基于它们的代码生成。问题是我的脚本错误,说我的当前项目中的类不能被访问。T4 Toolbox - 在当前程序集中引用类
脚本本身驻留在相同的组件,我想引用的类。我已经尝试引用名称空间,文件并添加对当前程序集(项目本身)的引用 - 都无济于事。
我错过了什么?
我相信这是力高和uosɐs正在寻找。只需使用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()
方法,该项目符合我们的搜索标准,其内解析出的类。
我希望示例代码为您提供了一个起点,从跳下 - 但如果你需要进一步的细节,这里有一些额外的参考,为您的牙齿沉入:
参考它常见的方式。然后检查程序集是否被加载,如果没有 - 生成存根代码(使编译成为可能;编译后再次运行T4以生成实际代码)。并有单元测试,可以防止存根代码生产。
出于某种原因,我无法获得@brian解决方案的工作。我结束了这样做 在我的情况下,T4Generators是我在同一解决方案中的独立项目。
<#@ assembly name="$(SolutionDir)\T4Generators\bin\Debug\T4Generators.dll" #>
<#@ import Namespace="T4Generators" #>
改为使用$(TargetPath)。这是dll的宏。 <#@程序集名称=“$(TargetPath)”#> <#@ import namespace =“MyAssembly.CodeGeneration”#> – 2013-01-18 20:51:52
如果您还保留原始解决方案 – 2017-03-25 01:15:46