我最近将一个测试项目迁移到.NET Core 2.0。该测试项目使用文本模板生成一些重复代码。之前的项目有一个构建目标,用于在构建之前生成所有 T4 模板。因此,生成的代码也不会签入 VCS。
我在项目中使用了此代码片段来确保构建模板:
<PropertyGroup>
<!-- Default VisualStudioVersion to 15 (VS2017) -->
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<!-- Determinate VSToolsPath -->
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<!-- Run T4 generation if there are outdated files -->
<TransformOnBuild>True</TransformOnBuild>
<TransformOutOfDateOnly>True</TransformOutOfDateOnly>
</PropertyGroup>
<!-- Import TextTemplating target -->
<Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" />
我的第一个方法是保留此片段并将其复制到新的 .NET Core 项目文件中。
在 Visual Studio 中,这是有效的,因为显然
VSToolsPath
设置正确。但是,当我运行 .NET Core SDK 工具时,例如 dotnet test
(正如我在构建服务器上所做的那样),VSToolsPath
映射到 Program Files\dotnet\sdk\2.0.3
,并且无法找到文本模板目标。
因为这不起作用,我还尝试简单地从 Nuget 安装
Microsoft.VisualStudio.TextTemplating
包,但这有两个问题:
要在构建
dotnet build
时支持构建 T4 模板,您需要使用 Custom Text Template Host
,它已经存在于 .NET Core 中 (https://github.com/atifaziz/t5)。要包含它,请将此元素添加到您的项目中的任意 ItemGroup
中:
<DotNetCliToolReference Include="T5.TextTransform.Tool" Version="1.1.0-*" />
。
由于 Visual Studio 已经拥有自己的 Text Template Host
实现,因此您添加的元素应仅适用于 .NET Core。例如:
<ItemGroup Condition="'$(MSBuildRuntimeType)'=='Core'">
<DotNetCliToolReference Include="T5.TextTransform.Tool" Version="1.1.0-*" />
</ItemGroup>
同时,您应该在 .NET Core 之外调整 Visual Studio 文本模板主机的设置,如下所示:
Condition="'$(MSBuildRuntimeType)'=='Full'"
。
您还应该在导入
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" Condition="'$(MSBuildRuntimeType)'=='Full'" />
之前添加 Microsoft.TextTemplating.targets
,以使一切在 Visual Studio 中与 .NET Core csproj
正常工作。
如果您需要清理所有生成的代码,您应该将模板从
*.tt
重命名为 *.Generated.tt
,所有代码将在 *.Generated.cs
下生成,并且可以在 过滤掉这些文件dotnet clean
行动。
它在您的
csproj
中的完整示例:
<!-- T4 build support for .NET Core (Begin) -->
<ItemGroup Condition="'$(MSBuildRuntimeType)'=='Core'">
<DotNetCliToolReference Include="T5.TextTransform.Tool" Version="1.1.0-*" />
<TextTemplate Include="**\*.Generated.tt" />
<Generated Include="**\*.Generated.cs" />
</ItemGroup>
<Target Name="TextTemplateTransform" BeforeTargets="BeforeBuild" Condition="'$(MSBuildRuntimeType)'=='Core'">
<ItemGroup>
<Compile Remove="**\*.cs" />
</ItemGroup>
<Exec WorkingDirectory="$(ProjectDir)" Command="dotnet tt %(TextTemplate.Identity)" />
<ItemGroup>
<Compile Include="**\*.cs" />
</ItemGroup>
</Target>
<Target Name="TextTemplateClean" AfterTargets="Clean">
<Delete Files="@(Generated)" />
</Target>
<!-- T4 build support for .NET Core (End) -->
<!-- T4 build support for Visual Studio (Begin) -->
<PropertyGroup Condition="'$(MSBuildRuntimeType)'=='Full'">
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<!-- This is what will cause the templates to be transformed when the project is built (default is false) -->
<TransformOnBuild>true</TransformOnBuild>
<!-- Set to true to force overwriting of read-only output files, e.g. if they're not checked out (default is false) -->
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
<!-- Set to false to transform files even if the output appears to be up-to-date (default is true) -->
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" Condition="'$(MSBuildRuntimeType)'=='Full'" />
<Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" Condition="'$(MSBuildRuntimeType)'=='Full'" />
<!-- T4 build support for Visual Studio (End) -->
如果您不想重命名模板文件并且不需要清理它们,则替换:
<TextTemplate Include="**\*.Generated.tt" />
<Generated Include="**\*.Generated.cs" />
与:
<TextTemplate Include="**\*.tt" />
并删除:
<Target Name="TextTemplateClean" AfterTargets="Clean">
<Delete Files="@(Generated)" />
</Target>
欲了解更多信息,请参阅:
如何在
dotnet build
上设置代码生成:
https://notquitepure.info/2018/12/12/T4-Templates-at-Build-Time-With-Dotnet-Core/
如何在 Visual Studio 和 .NET Core 的构建中设置代码生成
csproj
:
https://thomaslevesque.com/2017/11/13/transform-t4-templates-as-part-of-the-build-and-pass-variables-from-the-project/
从单个 T4 模板生成多个文件的完整示例: https://github.com/Konard/T4GenericsExample
更新:
你受制于为 dotnet core 编写端口的人。
这是旧的:http://www.bricelam.net/2015/03/12/t4-on-aspnet5.html
或者完全使用不同的模板工具,当然我理解如果这不是一个选择。
或者只使用T4执行器。您可以设置在构建之前、构建之后执行哪些模板或忽略特定模板。与 VS2017-19 配合良好
扩展Konard 的评论“更新:https://github.com/mono/t4 更好。”
如果您正在运行 Azure DevOps 管道,则可以将其添加为一个步骤 - 请参阅https://stackoverflow.com/a/60667867/1901857的第一部分。
如果您在 Linux dockerfile 中构建,请在构建之前添加此内容(我们在 alpine 上使用 .net 6,但其他发行版和版本应该没问题):
# you will see a warning if this folder is not on PATH
ENV PATH="${PATH}:/root/.dotnet/tools"
RUN dotnet tool install -g dotnet-t4
dotnet tool install -g dotnet-t4
dotnet-t4 的设置与 Konard 的回答中的 t5 非常相似
选项 1 - 在您的开发计算机上安装 dotnet-t4
将其添加到您的 csproj 文件中。无需条件设置。
<!-- T4 build support for .NET Core (Begin) -->
<ItemGroup>
<TextTemplate Include="**\*.tt" />
</ItemGroup>
<Target Name="TextTemplateTransform" BeforeTargets="BeforeBuild">
<ItemGroup>
<Compile Remove="**\*.cs" />
</ItemGroup>
<Exec WorkingDirectory="$(ProjectDir)" Command="t4 %(TextTemplate.Identity)" />
<ItemGroup>
<Compile Include="**\*.cs" />
</ItemGroup>
</Target>
<!-- T4 build support for .NET Core (End) -->
选项 2 - 在您的开发机器上使用 VS 模板
如果您不希望每个人都在本地安装该工具,您仍然可以按照 Konard 的回答向项目添加条件构建步骤。请注意,Visual Studio 现在是 64 位,因此您可以使用
MSBuildExtensionsPath
代替 MSBuildExtensionsPath32
:
<!-- T4 build support for .NET Core (Begin) -->
<ItemGroup Condition="'$(MSBuildRuntimeType)'=='Core'">
<TextTemplate Include="**\*.tt" />
</ItemGroup>
<Target Name="TextTemplateTransform" BeforeTargets="BeforeBuild" Condition="'$(MSBuildRuntimeType)'=='Core'">
<ItemGroup>
<Compile Remove="**\*.cs" />
</ItemGroup>
<Exec WorkingDirectory="$(ProjectDir)" Command="t4 %(TextTemplate.Identity)" />
<ItemGroup>
<Compile Include="**\*.cs" />
</ItemGroup>
</Target>
<!-- T4 build support for .NET Core (End) -->
<!-- T4 build support for Visual Studio (Begin) -->
<PropertyGroup Condition="'$(MSBuildRuntimeType)'=='Full'">
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<TransformOnBuild>true</TransformOnBuild>
<!--Other properties can be inserted here-->
<!--Set to true to force overwriting of read-only output files, e.g. if they're not checked out (default is false)-->
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
<!--Set to false to transform files even if the output appears to be up-to-date (default is true)-->
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>
<Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" Condition="'$(MSBuildRuntimeType)'=='Full'" />
<!-- T4 build support for Visual Studio (End) -->