c# - T4 模板中的 GetType() 始终返回 null
问题描述
我正在尝试编写一个 T4 模板,它将为文件夹中的每个模型类生成一个部分类文件。我可能做错了,所以请随时建议对似乎有效的位进行改进。
我的测试项目有一个模型文件夹,其中包含几个简单的类,例如Person.cs
包含...
using System;
namespace WithTT.Models {
public partial class Person {
public int ID { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
// etc...
}
}
我的 T4 模板(在同一文件夹中)使用MultipleOutputHelper.ttinclude帮助器模板。T4文件目前看起来像这样......
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ include file="MultipleOutputHelper.ttinclude" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".log" #>
<# var manager = Manager.Create(Host, GenerationEnvironment); #>
<# manager.StartHeader(false); #>
<# manager.EndBlock(); #>
<#
string folder = this.Host.ResolvePath("."); // get current folder
folder = folder.Substring(0, folder.Length - 2); // knock the "\." off the end
// Loop through each file and create an AbcExt.cs partial class
foreach(string file in Directory.GetFiles(folder, "*.cs").Where(f => !f.EndsWith("Ext.cs")).Select(f => f.Replace(".cs", ""))) {
manager.StartNewFile($"{file}Ext.cs");
string className = Path.GetFileNameWithoutExtension(file);
string ns = File.ReadAllLines(file + ".cs").Single(l => l.StartsWith("namespace")).Replace("namespace", "").Replace("{", "").Trim();
#>
// This code was generated from a template, do not modify!
namespace <#=ns#> {
public partial class <#=className#> {
// TODO AYS - Write the With() method...
}
}
<#
manager.EndBlock();
}
#>
<# manager.Process(true); #>
到目前为止,这工作正常,并生成如下所示的基本代码文件
// This code was generated from a template, do not modify!
namespace WithTT.Models {
public partial class Person {
// TODO AYS - Write the With() method...
}
}
现在,我需要用来生成我想要的代码的一些辅助方法需要相关类的类型。掌握了命名空间(在ns
变量中)和类名(在className
变量中)之后,我希望能够执行以下操作...
Type t = Assembly.GetExecutingAssembly().GetType(ns + "." + className);
...或者...
Type t = Type.GetType(ns + "." + className);
但是,无论我使用哪个,t
始终为空。命名空间和类名都很好,正如您从生成的文件中看到的那样,根据这个问题的前两个答案,任何一个都应该工作。
如果我在测试项目的 Program.cs 中尝试相同的代码,我得到的类型没有问题。
任何人都知道我怎样才能得到有Type
问题的班级?谢谢
编辑我意识到为什么类型总是空的。如果我将程序集命名空间转储到输出中,这样说...
// <#=Assembly.GetExecutingAssembly().FullName#>
...然后我看到TemporaryT4Assembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
这显然与保存类的程序集不同。我现在的问题是找出如何获取项目的程序集,而不是从 T4 模板生成的程序集
解决方案
结果很简单。这个 SO 答案显示了以下两行......
var path = this.Host.ResolveAssemblyReference("$(TargetPath)");
var asm = Assembly.LoadFrom(path);
一旦我加载了程序集,获取类型就非常简单了......
Type t = asm.GetType(fileNamespace + "." + className);
编辑我发现这种方法的一个主要缺点是Assembly.LoadFrom
锁定程序集,这意味着您无法在不重新启动 Visual Studio 的情况下重建项目。
另一种方法使用Assembly.Load(File.ReadAllBytes(path))
,它避免了这个问题(因为它Assembly
从实际程序集的副本创建),但要求您知道程序集的路径和名称。
就我的目的而言,以下的伎俩...
IServiceProvider hostServiceProvider = (IServiceProvider)Host;
EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
Array activeSolutionProjects = (Array)dte.ActiveSolutionProjects;
EnvDTE.Project dteProject = (EnvDTE.Project)activeSolutionProjects.GetValue(0);
string defaultNamespace = dteProject.Properties.Item("DefaultNamespace").Value.ToString();
string templateDir = Path.GetDirectoryName(Host.TemplateFile);
string fullPath = dteProject.Properties.Item("FullPath").Value.ToString();
fullPath = fullPath.EndsWith("\\") ? fullPath.Substring(0, fullPath.Length-1) : fullPath;
string exePath = fullPath + "\\bin\\Debug\\" + defaultNamespace + ".exe";
Assembly asm = Assembly.Load(File.ReadAllBytes(exePath));
...这是相当多的代码(尽管如果您比我现在更好地理解它,可能会清理它)。在正常情况下,Assembly.LoadFrom
不推荐,但在这里它工作正常。
这种方法的主要问题是我假设程序集在bin\Debug
文件夹中(不是一个糟糕的假设),与项目命名相同(不是一个很好的假设,但通常可能是正确的)并且是一个.exe
(相当糟糕的假设这经常是错误的)。最后一个假设可以通过在上面的代码中查找项目类型来缓解,但我没有时间尝试。我在这里留下评论,作为对任何复制代码的人的警告。
推荐阅读
- amazon-web-services - 如何从 shell 脚本确认 aws cloudformation validate-template 命令?
- reactjs - React 组件似乎因为子组件而重新渲染(但不应该)
- laravel - 照亮\数据库\查询异常 | SQLSTATE [42S21]:列已存在:1060 列名重复
- php - 当用户未登录无法看到页面 wordpress 时的功能
- vba - 按文件名查找形状图片
- python - 如何从python中的数据列中提取两个数字?
- c# - 在应该永远保持打开状态的 gRPC 流中返回新数据
- javascript - 不同时间启动功能
- python - grahql-ws 订阅 aiohttp:如何允许订阅?
- python - 当 Pandas 数据框中的列名重复时怎么办?