VS2013发布Web部署任务失败:文件正在使用中。

33
我正在使用VS2013 Premium将网站发布到Windows Server 2012。除了这些文件外,所有文件都可以正常发布:SqlServerTypes\x64\msvcr100.dll。

SqlServerTypes\x64\SqlServerSpatial110.dll

SqlServerTypes\x86\msvcr100.dll

SqlServerTypes\x86\SqlServerSpatial110.dll

对于我尝试发布的每个文件,都会出现以下类型的错误: Web部署任务失败。(文件“msvcr100.dll”正在使用中。请在http://go.microsoft.com/fwlink/?LinkId=221672#ERROR_FILE_IN_USE了解更多信息。)
有趣的是,这些文件第一次发布时(当它们不在服务器上时)可以成功发布,但之后就无法被覆盖。已经尝试过两个不同的Web服务器。 我已经按照这里的指南进行操作: http://blogs.msdn.com/b/webdev/archive/2013/10/30/web-publishing-updates-for-app-offline-and-usechecksum.aspx 但它只能让网站脱机(VS正在放置app_offline.htm),但发布仍然失败并且出现相同的错误。 所有其他文件都可以成功发布。
有任何想法吗?

我也遇到了这个问题。似乎无法解决。我们每次部署之前都会停止IIS。这似乎是唯一的可行方法。 - Erik5388
3
你尝试过使用Sysinternals的“handle”吗?它通常可以告诉你哪个进程占用了给定文件的句柄。你甚至可以运行该程序,获取输出,解析并释放锁定 - 但我可能有点过于急躁了 :) - Derek
根据我的经验,问题出在你的本地机器上。通常情况下,IIS Express是罪魁祸首,但有时重启/结束它并不能解决问题。我有时不得不完全关闭所有Visual Studio实例和/或删除Bin文件夹中的内容 - 我发现当你打开多个项目且代码在某种程度上共享时,这种问题很容易发生。 - Hugo Yates
我总是使用 Unlocker 工具 http://www.emptyloop.com/unlocker/ 来解决这种问题,以确定哪个进程正在使用或锁定文件。不过在安装过程中一定要小心不要选择广告软件! - Matthew Lock
是的,删除文件并删除对库的引用是第一步。我下面提供的问题修复方案并没有真正确定原因,但它可能会帮助某人在未来解决他们的问题。 - user3546827
显示剩余9条评论
5个回答

27

在发布期间,您可以将应用程序脱机,这样就可以释放文件上的锁定并允许您更新它。

我之前 发表了博客文章。支持概述已经包含在Azure SDK和Visual Studio Update中。我不记得确切的版本,但如果需要,我可以找出来。任何日期在/之后发布的更新都应该没问题。

先决条件:

  • VS 2012 + VS 更新 / VS 2013 + VS 更新 / VS2015
  • MSDeploy v3

注意:如果您正在从CI服务器发布,则CI服务器也需要上述更新

编辑发布配置文件

在VS中创建Web Publish配置文件时,对话框中的设置将存储在Properties\PublishProfiles\中,以.pubxml结尾的文件中。注意:还有一个.pubxml.user文件,该文件不应修改

要将应用程序脱机,请在.pubxml文件中添加以下属性。

<EnableMSDeployAppOffline>true</EnableMSDeployAppOffline>

注释

需要ASP.NET

MSDeploy实现的方式是在网站/应用程序的根目录中放置一个app_offline.htm文件。然后ASP.NET运行时会检测并将您的应用程序下线。因此,如果您的网站/应用程序未启用ASP.NET,则此功能将无法使用。

可能无法正常工作的情况

此实现使得应用程序在发布开始之前可能不会完全下线。首先会放置app_offline.htm文件,然后MSDeploy将开始发布文件。它不等待ASP.NET检测到文件并将其下线。因此,您可能会遇到仍然遇到文件锁定的情况。默认情况下,VS启用重试,因此通常应用程序会在其中一个重试期间下线,一切都很好。在某些情况下,ASP.NET的响应时间可能会更长。那就有点棘手了。

如果你添加了<EnableMSDeployAppOffline>true</EnableMSDeployAppOffline>,但是应用程序还没有被很快地下线,那么我建议在发布之前将应用程序下线。有几种远程操作的方法,但这取决于你的设置。如果你只有MSDeploy访问权限,可以尝试以下步骤:
  1. 使用msdeploy.exe通过放置app_offline.htm来使您的站点下线
  2. 使用msdeploy.exe发布您的应用程序(确保同步不会删除app_offline.htm文件)
  3. 等待一段时间
  4. 发布网站
  5. 使用msdeploy.exe通过删除app_offline.htm来上线应用程序
我曾经在http://sedodream.com/2012/01/08/howtotakeyourwebappofflineduringpublishing.aspx中发布了如何进行此操作的博客。该博客文章唯一缺少的是等待网站实际下线的延迟时间。您还可以创建一个脚本,直接调用msdeploy.exe,而不是将其集成到项目构建/发布过程中。

2
实际上,解决方案在您的答案和这里的大部分评论中。基本上,释放文件上的锁定是必要的第一步。然后我决定删除 SQL Server 空间类型(Microsoft 的空间类型)包并删除有问题的文件。然后我尝试了 SQL Server 空间类型 (#foo) NuGet 包,因为我需要这些函数。不幸的是,虽然此包在发布方面没有问题,但使用这些库的代码的行为不正确。然后我删除了它并回到了 Microsoft 包。现在发布正常运行,没有更多错误。 - user3546827

10

我已经找到了解决方案为什么在http://blogs.msdn.com/b/webdev/archive/2013/10/30/web-publishing-updates-for-app-offline-and-usechecksum.aspx的链接中并未适用于原作者,并且我有一个解决方法。

EnableMSDeployAppOffline的问题在于它只会重启托管应用程序的应用程序域,而不会重新启动应用程序域所在的应用程序池工作进程(w3wp.exe)。

拆除和重新创建应用程序域不会影响涉及的Sql Server Spatial dlls。这些dll是通过interop LoadLibray调用手动加载的非托管代码。因此,这些dll位于应用程序域范围之外。

为了释放文件锁定,您需要回收应用程序池或手动从内存中卸载dll。

Microsoft.SqlServer.Types nuget包提供了一个类,用于加载空间dlls,名为SqlServerTypes.Utilities。您可以修改LoadNativeAssemblies方法以在卸载应用程序域时卸载非托管dll。通过这个修改,当msdeploy复制app_offline.htm时,应用程序域将卸载,然后也会卸载托管dll。

[DllImport("kernel32.dll", SetLastError = true)]
internal extern static bool FreeLibrary(IntPtr hModule);

private static IntPtr _msvcrPtr = IntPtr.Zero;
private static IntPtr _spatialPtr = IntPtr.Zero;

public static void LoadNativeAssemblies(string rootApplicationPath)
{
    if (_msvcrPtr != IntPtr.Zero || _spatialPtr != IntPtr.Zero)
        throw new Exception("LoadNativeAssemblies already called.");

    var nativeBinaryPath = IntPtr.Size > 4
        ? Path.Combine(rootApplicationPath, @"SqlServerTypes\x64\")
        : Path.Combine(rootApplicationPath, @"SqlServerTypes\x86\");

    _msvcrPtr = LoadNativeAssembly(nativeBinaryPath, "msvcr100.dll");
    _spatialPtr = LoadNativeAssembly(nativeBinaryPath, "SqlServerSpatial110.dll");

    AppDomain.CurrentDomain.DomainUnload += (sender, e) =>
    {
        if (_msvcrPtr != IntPtr.Zero)
        {
            FreeLibrary(_msvcrPtr);
            _msvcrPtr = IntPtr.Zero;
        }

        if (_spatialPtr != IntPtr.Zero)
        {
            FreeLibrary(_spatialPtr);
            _spatialPtr = IntPtr.Zero;
         }
    };
}

这种方法有一个需要注意的地方。它假设你的应用程序是使用空间dll运行的工作进程中唯一运行的。由于应用程序池可以托管多个应用程序,如果另一个应用程序也加载了它们,则文件锁定将不会释放。这将导致您的部署出现相同的文件锁定错误。


我的情况下这是理想的解决方案。它允许我们在不需要部署用户在IIS服务器上拥有权限的情况下进行部署,而其他提出的解决方案则需要这样做。 - Sangman

1

IIS存在已知的文件锁问题(为什么它们还没有解决我不知道)。

然而,我想问的问题是,您是否需要重新部署这些文件?

我认识这些文件名,并记得它们应该是系统文件,应该已经存在于服务器上,或者根本不需要重新部署。

当涉及到IIS时,我并不是很有经验,但我之前遇到过这个问题,我的几位更有经验的同事告诉我,这是一个已知的IIS问题,我相信你问题的答案是:

  1. 避免部署不必要的文件。
  2. 再试一次。
  3. 重置网站。
  4. 再试一次。
  5. iisreset。

1

我认为最简单的方法是将这些dll标记为CopyLocal为true。我假设这些dll是从程序文件夹中提取出来的。尝试将它们标记为copylocal true并进行部署。请尝试停止在您的本地计算机上运行的任何IIS本地进程。


0

注意,你不要运行那些占用文件锁的新型云备份服务,也不要在资源管理器或DLL检查工具中打开文件。

我认为微软没有为这个问题提供更好的解决方案有点荒谬。我发现,大多数情况下我的部署都能正常工作,但随着我们的流量增加,成功率可能会降低。

我将通过以下方式解决这个问题:

  • 创建两个应用程序MySite.AMySite.B,每次只运行一个。
  • 我总是部署到休眠站点。
  • 如果在部署过程中出现问题,它永远不会导致整个站点崩溃。
  • 如果部署后出现重大问题,可以很容易地恢复到之前的状态。

我还不确定如何实现它,但我认为这就是我需要做的。


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