MSBuild锁定了在构建时生成的T4模板所使用的dll。

11

我在尝试识别为什么MSBuild正在阻止访问我刚创建的一个新T4模板中使用的dll时遇到了很多麻烦。

问题有点难以解释(正如标题所示)。

我创建了一个T4模板,用于生成一个包装我们拥有的N个其他类的c#类。这是我想出来的解决方案,以在同一端点上公开多个WCF服务

模板代码本身使用了一个包含各种扩展方法的程序集(Mobiltec.Framework.dll),以简化模板代码。起初,我只是将.tt文件添加到项目中并继续使用:每当.tt更改时,它都会更新生成的文件,就像预期的那样。

由于此模板读取其他程序集并基于它们生成类,因此我希望确保它在每次构建时转换输出,以防止开发人员忘记更新输出等事情。

经过几天的搜索,我终于找到了一个相当不错的解决方案,模板在每次构建时都被转换得“几乎”完美。问题是,每当我构建项目并转换模板时,它就会锁定对前面提到的dll的访问,从而防止我在之后删除或更新文件。我必须关闭Visual Studio才能释放该文件。

现在,在本地开发人员机器上,这不是一个很大的问题,但它会使我们的构建失败,因为输出都在同一个文件夹中:

  1. 构建过程开始编译我们的主解决方案
  2. 包含T4模板的项目被编译,并生成了类
  3. 另一个项目被编译,并尝试更改输出文件夹中的dll,经过10次尝试后构建失败

这是文本模板系统中的错误还是我可能犯了一些错误导致文件被锁定?

更新

我将尝试更好地解释这里的所有设置,以便您们可以更好地理解问题。

有一个.tt模板文件,总共有四个Web应用程序(其中有4个)。此模板负责自动生成所有WCF服务契约的包装器类。合同本身分为另一个程序集(也是每个组件的一个)中,以便客户端可以共享而不携带所有其他依赖项。

我不得不实现一堆方法,使用反射来打印来自每个程序集的签名、成员和类型,以生成可以编译为有效类的文本。我在一个公共程序集上实现了这些方法。

然后,每个tt文件都引用此公共程序集,以及服务合同程序集,以生成此包装器类。例如,模板有一堆像这样的行:

<#@ assembly name="Mobiltec.M3.EG.Services.dll" #>
<#@ assembly name="Mobiltec.M3.Common.dll" #>

由于我们需要在每次构建时转换这些模板,我已更改了项目文件以实现这一点,并安装了Visual Studio SDK和Visualization and Modeling SDK,按照此建议进行操作。

现在,每当我对任何程序集或其依赖项中的代码进行更改时,都会收到以下消息:

Error   1516    Compiling transformation: Metadata file 'Mobiltec.M3.EG.Services.dll' could not be found. Line=0, Column=0  Mobiltec.M3.EG
Error   1517    Compiling transformation: Metadata file 'Mobiltec.M3.Common.dll' could not be found. Line=0, Column=0   Mobiltec.M3.EG

或者:

Error   395 Could not copy "D:\TFS05\M3\Desenvolvimento\Feature-ODataM3S\ProjetosManutencao\Mobiltec.M3.Common\Mobiltec.M3.Common.Desktop\bin\Mobiltec.M3.Common.dll" to "bin\Mobiltec.M3.Common.dll". Exceeded retry count of 10. Failed.  Mobiltec.M3.EG
Error   396 Unable to copy file "D:\TFS05\M3\Desenvolvimento\Feature-ODataM3S\ProjetosManutencao\Mobiltec.M3.Common\Mobiltec.M3.Common.Desktop\bin\Mobiltec.M3.Common.dll" to "bin\Mobiltec.M3.Common.dll". The process cannot access the file 'bin\Mobiltec.M3.Common.dll' because it is being used by another process.    Mobiltec.M3.EG
Error   407 Could not copy "D:\TFS05\M3\Desenvolvimento\Feature-ODataM3S\ProjetosManutencao\Mobiltec.M3.EG\Mobiltec.M3.EG.Services\bin\Debug\Mobiltec.M3.EG.Services.dll" to "bin\Mobiltec.M3.EG.Services.dll". Exceeded retry count of 10. Failed. Mobiltec.M3.EG
Error   408 Unable to copy file "D:\TFS05\M3\Desenvolvimento\Feature-ODataM3S\ProjetosManutencao\Mobiltec.M3.EG\Mobiltec.M3.EG.Services\bin\Debug\Mobiltec.M3.EG.Services.dll" to "bin\Mobiltec.M3.EG.Services.dll". The process cannot access the file 'bin\Mobiltec.M3.EG.Services.dll' because it is being used by another process.  Mobiltec.M3.EG

此外,当我尝试通过Visual Studio删除bin/obj文件夹时,会出现以下错误:

Visual Studio Deletion error

如果我手动尝试在Windows资源管理器中删除bin文件夹中的文件,则会出现以下错误:

Windows Explorer Deletion error

基本上,这归结为MSBuild锁定了Web应用程序项目的bin文件夹中所有明确引用的文件(.tt中的所有文件)。每当项目重建时,引用的dll被重新复制到项目自己的输出文件夹中,由于Windows无法覆盖“正在使用”的文件,因此此过程失败。

唯一能释放锁定的方法就是完全关闭Visual Studio并重新打开它。我尝试手动终止机器上的所有MSBuild进程或关闭并重新打开解决方案,但都不起作用。即使像Unlocker这样的工具也无法删除文件上的锁定(甚至无法检测到它)。

构建服务器上的最初问题也是由相同的问题引起的,但现在已经“解决”,因为我重命名了一个dll。两个不同的项目(Mobiltec.Framework.DesktopMobiltec.Framework.WindowsMobile)具有相同的输出dll名称(Mobiltec.Framework.dll),在.tt文件进行转换后,第二个项目尝试覆盖构建全局输出文件夹中的第一个项目(锁定原始的dll),导致整个过程失败。


也许我缺少些什么,但如果.dll包含辅助方法,为什么另一个项目会试图更改输出文件夹中的.dll呢? - Mike Beeler
模板如何“读取其他程序集”? - Nicole Calinoiu
@MikeBeeler 我已经更新了问题,并提供了更详细的信息,请看一下。 - julealgon
@NicoleCalinoiu 我已经更新了问题并提供了更详细的信息,请查看。 - julealgon
3个回答

8

略微相关,谷歌将我带到这里,解决一个非常类似的问题。调试t4模板时,如果抛出异常,会导致T4VSHostProcess.exe锁定模板中引用的所有DLL。重启Visual Studio可以解决问题,但我只需使用Sysinternals进程资源管理器手动终止T4VSHostProcess.exe进程即可避免重启Visual Studio。


0
如果我理解您的意思正确,这个问题可能已经在VS2010sp1中得到了修复,具体可以参考这里。但是另一方面,您可能会遇到找不到DLL的问题。

那个修复程序是从2010年12月的。当我提出这个问题(很久以前)时,我使用的Visual Studio版本比那个要更新得多,所以我认为它与此无关,不幸的是。 - julealgon
1
正在运行VS2017(15.9.5),仍然遇到相同的问题。'Debug T4 Template'在调试会话(带有异常)后锁定程序集。[2c] - obiwanjacobi
我现在是2022年,这种情况仍然发生。在VS之外尝试dotnet build,但由于TT引用的项目输出被锁定,它会卡住。 - undefined

0

您是否在使用多核构建?可能默认启用。请使用 /nr:false 参数来使用 msbuild。 这是一项可能会强制锁定文件的功能之一。


我在Visual Studio中该在哪里设置这个?另外,难道没有一种方法可以预防最初的锁定吗?如果这样做,我想我会失去构建时间,对吧? - julealgon
1
我刚想起来以前看到过这个。'nr'是指“节点重用”,对吧?在一定时间内保留MSBuild的实例,以提高性能。我之前尝试启用它(通过环境变量),但没有成功。 - julealgon
在进一步研究此问题后,似乎即使单线程也可能会引起该问题。已知存在Microsoft文章,在识别实际锁定过程方面存在问题。http://blogs.technet.com/b/markrussinovich/archive/2005/04/24/the-case-of-the-mysterious-locked-file.aspx - Mike Beeler
很不幸,你所链接的文章中的问题与我这里的实际问题完全没有关联。在我的情况下,它是完全确定性的:每次我更改文件时,MSBuild都会锁定dll文件。这不是仅在某些随机条件下发生几次的事情。 - julealgon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接