C# T4模板中的GetType()始终返回null

C# T4模板中的GetType()始终返回null,c#,reflection,t4,C#,Reflection,T4,我正在尝试编写一个T4模板,它将为文件夹中的每个模型类生成一个部分类文件。我可能做错了,所以请随意建议对似乎有效的位进行改进 我的测试项目有一个Models文件夹,其中包含两个简单的类,例如Person.cscontains using System; namespace WithTT.Models { public partial class Person { public int ID { get; set; } public string FirstName { ge

我正在尝试编写一个T4模板,它将为文件夹中的每个模型类生成一个部分类文件。我可能做错了,所以请随意建议对似乎有效的位进行改进

我的测试项目有一个Models文件夹,其中包含两个简单的类,例如
Person.cs
contains

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模板(在同一文件夹中)使用帮助器模板。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); #>
// <#=Assembly.GetExecutingAssembly().FullName#>
现在,我需要使用一些helper方法来生成我在这里想要的代码,这些方法需要所讨论的类的类型。在掌握了名称空间(在
ns
变量中)和类名(在
className
变量中)之后,我希望能够执行以下操作

Type t = Assembly.GetExecutingAssembly().GetType(ns + "." + className);
……或者

Type t = Type.GetType(ns + "." + className);
然而,无论我使用哪一个,
t
总是空的。名称空间和类名很好,正如您从生成的文件中看到的,根据前面两个答案,两者都可以工作

如果我在测试项目的Program.cs中尝试相同的代码,那么我就可以毫无问题地得到类型

有人知道我怎样才能得到这个类的
类型吗?谢谢

编辑我意识到为什么类型总是空的。如果我将程序集名称空间转储到输出中,可以这样说

<#@ 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); #>
// <#=Assembly.GetExecutingAssembly().FullName#>
//

…然后我看到
TemporaryT4Assembly,Version=0.0.0,Culture=neutral,PublicKeyToken=null
,这显然与保存类的程序集不同。我现在的问题是找到如何获得项目的组装,而不是从T4模板生成的组装,结果非常简单。显示以下两行

var path = this.Host.ResolveAssemblyReference("$(TargetPath)");
var asm = Assembly.LoadFrom(path);
一旦我加载了程序集,获取类型就非常简单了

Type t = asm.GetType(fileNamespace + "." + className);
EDIT我发现了这种方法的一个主要缺点,那就是
Assembly.LoadFrom
会锁定程序集,这意味着在不重新启动Visual Studio的情况下无法重建项目

另一种方法使用
Assembly.Load(File.ReadAllBytes(path))
,这可以避免此问题(因为它从实际程序集的副本创建
程序集
),但要求您知道程序集的路径和名称

为了我的目的,下面的方法成功了

  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
(这是一个非常糟糕的假设,经常会出错)。最后一个假设可以通过在上面的代码中查找项目类型来缓解,但我还没有时间尝试。我在这里留下评论作为对复制代码的任何人的警告。

可能尝试使用类型为@weichch的程序集限定名称:@weichch感谢您的建议,但T4模板不知道程序集名称。我意识到编译后的模板代码与主代码处于不同的程序集形式,这大概就是类型为null的原因,但我不知道如何获取主代码的程序集。有什么想法吗?感谢您首先必须加载程序集,然后如果
GetType
仍然不适用于您,则需要使用限定名称
GetType(string)
仅适用于限定名称,或者如果类型位于当前执行程序集或mscorlib中。而且,它区分大小写。我不确定在您的情况下执行的程序集是什么,您可以尝试:)