求助:在T4模板中加载.NET 8程序集时的依赖缺失与编译错误解决
求助:在T4模板中加载.NET 8程序集时的依赖缺失与编译错误解决
问题背景
我之前用T4模板在.NET Framework 4.8环境下正常解析程序集,但现在要加载一个.NET 8的类库(LibToLoad.dll)时遇到了麻烦:
- 直接加载时抛出
FileNotFoundException,提示找不到System.Runtime, Version=8.0.0.0 - 尝试引入EF的工具文件(
EF.Utility.CS.ttinclude/EF6.Utility.CS.ttinclude)要么无效,要么引发新的编译错误(CS0103: The name 'ArgumentNotNull' does not exist in the current context)
我的T4模板代码:
<#@ template language="C#" debug="false" hostspecific="true" #> <#@ output extension=".g.cs" #> <#@ include file="EF.Utility.CS.ttinclude" #> <#@ assembly name="SOME_PATH\net8.0\LibToLoad.dll" #> <# var baseType = typeof(LibToLoad.BaseClass); var asmQN = baseType.AssemblyQualifiedName; // var code = new CodeGenerationTools(this); // 后续想使用的EF工具类 #>
要加载的.NET 8类库的csproj:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> </PropertyGroup> </Project>
类代码:
namespace LibToLoad { public abstract class BaseClass { } public partial class MyClass1 : BaseClass { public System.Guid Uid; } }
问题根源
Visual Studio默认的T4模板引擎是基于**.NET Framework**的,而.NET 8的程序集依赖的是.NET Core/5+的共享框架体系,两者的依赖加载机制不兼容:
- .NET Framework的程序集加载器无法识别.NET 8的
System.Runtime这类核心库(.NET Framework自身的基础依赖和.NET Core完全不同) - 旧版EF的
*.Utility.CS.ttinclude是为.NET Framework+EF设计的,和.NET 8的环境不兼容,依赖的内部方法(比如ArgumentNotNull)在当前上下文不存在
解决方案
方案1:在.NET Framework的T4引擎中手动处理程序集解析
既然默认T4引擎是.NET Framework的,我们可以通过注册程序集解析回调,手动加载.NET 8的依赖库:
修改你的T4模板如下:
<#@ template language="C#" debug="true" hostspecific="true" #> <#@ output extension=".g.cs" #> <#@ assembly name="System.IO" #> <#@ assembly name="System.Runtime.Loader" #> <#@ import namespace="System" #> <#@ import namespace="System.IO" #> <#@ import namespace="System.Reflection" #> <# // 注册程序集解析回调,手动加载.NET 8的依赖 AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { var assemblyName = new AssemblyName(args.Name); // 1. 先尝试从你的LibToLoad输出目录加载依赖 var libOutputPath = @"SOME_PATH\net8.0"; // 替换成你的LibToLoad的实际输出路径 var libDependencyPath = Path.Combine(libOutputPath, $"{assemblyName.Name}.dll"); if (File.Exists(libDependencyPath)) { return Assembly.LoadFrom(libDependencyPath); } // 2. 如果找不到,从.NET 8的共享框架目录加载 var dotNet8InstallPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", "shared", "Microsoft.NETCore.App", "8.0.0" // 替换成你安装的.NET 8具体版本号 ); var frameworkDependencyPath = Path.Combine(dotNet8InstallPath, $"{assemblyName.Name}.dll"); if (File.Exists(frameworkDependencyPath)) { return Assembly.LoadFrom(frameworkDependencyPath); } return null; }; // 手动加载你的.NET 8程序集(不要用<#@ assembly #>指令) var libAssemblyPath = Path.Combine(libOutputPath, "LibToLoad.dll"); var targetAssembly = Assembly.LoadFrom(libAssemblyPath); var baseType = targetAssembly.GetType("LibToLoad.BaseClass"); var asmQN = baseType.AssemblyQualifiedName; // 如果你需要类似EF的CodeGenerationTools,自己实现简易版本或用CodeDom // 示例:自己实现参数检查(替代EF的ArgumentNotNull) void ArgumentNotNull(object value, string paramName) { if (value == null) throw new ArgumentNullException(paramName); } #>
方案2:改用.NET 8原生的T4引擎(推荐)
如果你的项目已经全面转向.NET 8,更彻底的方法是使用.NET 8的T4引擎来运行模板,完全避开.NET Framework的兼容问题:
- 创建一个新的SDK风格的.NET 8类库项目(用来承载T4模板)
- 安装NuGet包:
Microsoft.VisualStudio.TextTemplating.MSBuild(版本尽量和你的VS版本匹配,比如17.8.x) - 在项目的csproj中添加以下配置,让MSBuild用.NET 8引擎编译T4模板:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.VisualStudio.TextTemplating.MSBuild" Version="17.8.3" PrivateAssets="all" /> <TextTemplate Include="**\*.tt" /> </ItemGroup> <Target Name="TransformTemplates" BeforeTargets="Build"> <MSBuild Projects="$(MSBuildProjectFile)" Targets="TransformAllT4Templates" Properties="TextTemplatingEngine=Core" /> </Target> </Project>
- 将你的T4模板文件添加到这个项目中,直接加载.NET 8程序集即可,无需额外处理依赖:
<#@ template language="C#" debug="true" hostspecific="true" #> <#@ output extension=".g.cs" #> <#@ assembly name="SOME_PATH\net8.0\LibToLoad.dll" #> <#@ import namespace="LibToLoad" #> <# var baseType = typeof(BaseClass); var asmQN = baseType.AssemblyQualifiedName; // 现在可以正常使用反射或EF的.NET 8工具类 #>
方案3:让LibToLoad兼容.NET Framework和.NET 8(可选)
如果你愿意修改LibToLoad的源码,可以让它同时支持.NET Framework 4.8和.NET 8,这样T4模板(.NET Framework引擎)可以直接加载兼容版本:
修改LibToLoad的csproj:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>net8.0;net48</TargetFrameworks> </PropertyGroup> </Project>
然后T4模板中加载net48版本的LibToLoad.dll,这样就不会有.NET Core依赖的问题了。
关于EF工具类的替代
旧版EF的CodeGenerationTools等工具是为.NET Framework设计的,在.NET 8环境下建议替换为:
- 原生的
System.CodeDomAPI生成代码 - 使用Roslyn(
Microsoft.CodeAnalysisNuGet包)进行更灵活的代码分析与生成
内容来源于stack exchange
相关产品推荐
相关产品推荐

