【问题标题】:Visual Studio "Add As Link" not working while debuggingVisual Studio“添加为链接”在调试时不起作用
【发布时间】:2012-12-01 16:17:11
【问题描述】:

我使用 Visual Studio 2010 维护大约 40 个不同的 Web 应用程序,这些应用程序在部署时都位于同一个网站上。这些项目在同一个解决方案中,但位于不同的项目中,因为它们各自做的事情非常不同。

我正在尝试通过“添加为链接”选项在各个项目之间共享 css/js 文件,这样我可以一次更新 css 或 js 文件并自动将更新反映在各个项目中,而无需构建并重新部署每个项目。

我遇到的问题是,当我尝试在我的 PC 上本地运行项目以进行调试时,这些链接文件无法正常工作。找不到文件。

我的想法:我认为这是因为在本地运行应用程序时文件夹结构不同。我很好奇是否有一种方法可以仅在以调试模式构建时将文件复制到项目并相应地调整相对 URL,以便我能够在发布到我们的 Web 服务器之前正确测试应用程序。

谢谢,

【问题讨论】:

  • 只是出于好奇,链接是什么样的?我猜如果它实际上是一条直接路径,这就是为什么你不能在本地处理它的原因。
  • @lyaTaisho 你是对的,它没有设置为 .NET 样式的链接。它仅使用类似于“../../../SharedProjectFolder/css/style.css”的基本相对 URL
  • 相同的文件结构,都在你的本地机器上?
  • @lyaTaisho 是的,它与我本地盒子上的结构相同。
  • 解决这个问题的好方法:stackoverflow.com/a/17428953/114029

标签: asp.net asp.net-mvc-3 visual-studio-2010 web webforms


【解决方案1】:

也可以通过将以下 sn-p 粘贴到包含链接文件的 .proj 文件的末尾来解决

<Target Name="CopyLinkedContentFiles" BeforeTargets="Build">
<Copy SourceFiles="%(Content.Identity)" 
      DestinationFiles="%(Content.Link)" 
      SkipUnchangedFiles='true' 
      OverwriteReadOnlyFiles='true' 
      Condition="'%(Content.Link)' != ''" />

【讨论】:

    【解决方案2】:

    将链接文件复制到虚拟位置存在一个大问题:如果您在指向其中一个链接文件的 JavaScript 引用上使用 Go To Definition (Shift + F2),您将被带到本地复制的文件,而不是链接文件。那么,你一定会犯编辑本地版本的错误,从而消除使用链接文件的好处。此外,这可能会导致 Intellisense 出现问题。

    更好的解决方案:不要将链接文件复制到当前项目目录以及相关链接,而是将它们复制到该目录中的“hiddenDebug”文件夹(或您希望的任何目录;将其保存在同一目录中更容易管理,然后在调试期间操纵路径,我将在下面解释)。

    以下是如何将文件从共享存储库复制到“hiddenDebug”文件夹(添加到源项目的 Post-Build 事件):

    单个 CSS 文件

    xcopy /Y "$(ProjectDir)App_Themes\Theme1\Shared.css" "$(SolutionDir)WebApp\App_Themes\Theme1\hiddenDebug\"

    JavaScript 目录

    xcopy /Y /S "$(ProjectDir)Scripts" "$(SolutionDir)WebApp\Scripts\Shared\hiddenDebug\"

    在调试时,您可以使用 Global.asax 中的 Response.Filter 动态更改共享文件的源路径。这是一个例子:

    响应过滤器类(在共享项目中)

    Imports System.IO
    
    Namespace Code
        Public Class LinkedReferencesFilter
        Inherits MemoryStream
    
            Private ReadOnly outputStream As Stream = Nothing
            Private ReadOnly _IsDevEnvironment As Boolean = False
    
            Public Sub New(ByVal output As Stream, IsDevEnvironment As Boolean)
                Me.outputStream = output
                Me._IsDevEnvironment = IsDevEnvironment
            End Sub
    
            Public Overrides Sub Write(ByVal buffer As Byte(), ByVal offset As Integer, ByVal count As Integer)
                ' Convert the content in buffer to a string
                Dim contentInBuffer As String = UTF8Encoding.UTF8.GetString(buffer)
    
                If Me._IsDevEnvironment Then
                    contentInBuffer = contentInBuffer.Replace("<script src=""Scripts/Shared/", "<script src=""Scripts/Shared/hiddenDebug/")
                    contentInBuffer = contentInBuffer.Replace("/Scripts/Shared/", "/Scripts/Shared/hiddenDebug/")
                    contentInBuffer = contentInBuffer.Replace("/App_Themes/Theme1/Shared.css", "/App_Themes/Theme1/hiddenDebug/Shared.css")
                End If
    
                Me.outputStream.Write(UTF8Encoding.UTF8.GetBytes(contentInBuffer), offset, UTF8Encoding.UTF8.GetByteCount(contentInBuffer))
            End Sub
        End Class
    End Namespace
    

    Global.asax

    Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
        ' Simulate internet latency on local browsing
        If Request.IsLocal Then
            System.Threading.Thread.Sleep(50)
        End If
    
        Dim currentRelativePath As String = Request.AppRelativeCurrentExecutionFilePath
    
        If request__1.HttpMethod = "GET" Then
            If currentRelativePath.EndsWith(".aspx") Then
                Dim IsDevEnvironment As Boolean = False
                //Use whatever method you want to determine whether your current environment is a development environment:
                #If CONFIG = "Develop" Then
                    IsDevEnvironment = True
                #End If
    
                Response.Filter =
                    New Shared.Code.LinkedReferencesFilter(
                        output:=Response.Filter,
                        IsDevEnvironment:=IsDevEnvironment)
            End If
        End If
    End Sub
    

    疑难解答:尝试卸载并重新加载包含链接项目的项目。如果这没有帮助,请将 hiddenDebug 目录添加到您的项目中。我必须这样做,但后来我能够将其删除。太挑剔了……如果微软完善了这个功能就好了,但我现在已经准备好了。

    如果您不知道:当您发布 Web 应用程序时,源(链接)文件会自动复制到发布目标。一旦设置好,您就可以忘记它。最棒的是,您不会失去智能感知或快速导航。

    迁移到 TypeScript 后,大多数 JavaScript 文件引用挑战将被简化或消除(我希望能够像其他托管 .NET 语言一样轻松实现跨项目引用,但可能已经有一些解决方法来启用此类功能)。

    请告诉我它是否适合您,或者您是否有更好的方法。

    【讨论】:

    • 提出来的好点。我认为对于大多数人来说,他们从 nuget 包中链接资产文件,这些包永远不应该被修改,如果必须修改它们应该在单独的文件中被覆盖。上面提到的方法消除了代码签入的混乱,因此开发人员只需要担心他们维护的代码。
    【解决方案3】:

    此问题的解决方案是复制在每次构建期间作为链接添加的内容文件(如 js、css 或其他)。有几种方法可以做到这一点。我可以建议使用 MSBuild 目标,它可以被不同的 Web 应用程序项目重用。

    因此,您可以使用以下内容创建以下文件(例如,将其命名为 WebApplication.Extension.targets):

    <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    
        <!-- Override the default target dependencies to -->
        <!-- include the new CopyLinkedContentFiles target. -->
        <PropertyGroup>
            <BuildDependsOn>
                CopyLinkedContentFiles;
                $(BuildDependsOn);
            </BuildDependsOn>
        </PropertyGroup>
    
        <!--
        ============================================================
        CopyLinkedContentFiles
    
        A new target to copy any linked content files into the 
        web application output folder.
    
        NOTE: This is necessary even when '$(OutDir)' has not been redirected.
        ============================================================
        -->
        <Target Name="CopyLinkedContentFiles">
            <!-- Remove any old copies of the files -->
            <Delete Condition=" '%(Content.Link)' != '' AND Exists('$(WebProjectOutputDir)\%(Content.Link)') "
                    Files="$(WebProjectOutputDir)\%(Content.Link)" />
            <!-- Copy linked content files recursively to the project folder -->
            <Copy Condition=" '%(Content.Link)' != '' " SourceFiles="%(Content.Identity)"
                  DestinationFiles="$(WebProjectOutputDir)\%(Content.Link)" />
        </Target>
    </Project>
    

    然后通过将以下行放入 .csproj 文件中,将此目标添加到您的 Web 应用程序项目中:

    <Import Project="$(MSBuildProjectDirectory)[RelativePathToFile]\WebApplication.Extension.targets" />
    

    基本上你可以在 .csproj 文件中在以下一行之后添加这一行:

    <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
    

    应该解决作为链接添加的内容文件的问题。

    编辑: 要仅在 MSBUILD 中构建调试配置时执行以下逻辑,您可以指定 Condition 元素。

    例如,仅导入指定目标以进行调试配置,您可以将导入语句更新为以下内容:

    <Import Project="$(MSBuildProjectDirectory)[RelativePathToFile]\WebApplication.Extension.targets" Condition=" '$(Configuration)' == 'Debug' "/>
    

    编辑2:

    前段时间为了克服这个问题,我创建了一个“MSBuild.WebApplication.CopyContentLinkedFiles”nuget 包。这个包添加了 MsBuild 目标,它将在构建期间作为链接添加到项目文件夹的所有内容文件复制。

    【讨论】:

    • 这样做是否只会在调试过程中复制文件,而不是在发布构建期间将文件发布到网络服务器并且相关 URL 应该可以工作?我不希望每个项目都有自己的 JS 和 CSS 文件副本。感谢您的回答。
    • @Chattah,我更新了我的答案以回答如何使提供的解决方案仅用于调试配置。请参阅编辑部分。
    • 这有一个警告。例如,如果您链​​接文件、添加此目标并构建项目,则会复制内容文件(例如 aspx)。如果您稍后删除链接的文件,复制的文件将保留,因为它们从来不是解决方案的一部分。对于网站,这会导致 aspx 文件剩余,并且代码隐藏不再存在,因此不会被编译。当您运行 aspnet_compiler.exe(用于预编译)时,它将获取 aspx 文件,对其进行解析,并抱怨它无法加载与不存在的代码隐藏关联的类型。在构建服务器上,这很难修复。
    • 即使您没有编译错误,您也可能包含不应该存在的旧链接文件,例如 HTML 内容等。解决此问题的一种方法是完全清理文件而不是在构建之前进行源代码控制(使用 TFS:tfpt scorch),但这当然会增加您的构建时间。
    • @MaximKornilov 喜欢你用 NuGet 包回答了一个帖子...就在那里,OP 提供 web 问题的 jsbin 示例;)
    【解决方案4】:

    使用 MSBuild 在每次构建时自动将所有链接的内容文件复制到其“虚拟”位置的解决方案如下:

    http://mattperdeck.com/post/Copying-linked-content-files-at-each-build-using-MSBuild.aspx

    这可确保当您按 F5 时,您的链接文件将可供浏览器使用。

    【讨论】:

    • 这是一个很好的解决方案,值得更多关注!
    • 我同意!非常简单的修复,如果您使用 TFS 进行连续构建,它似乎不会影响 TFS 构建。
    猜你喜欢
    • 2013-09-28
    • 1970-01-01
    • 2023-01-25
    • 2016-10-30
    • 1970-01-01
    • 2015-10-13
    • 1970-01-01
    • 1970-01-01
    • 2018-03-22
    相关资源
    最近更新 更多