28

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

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

我遇到的问题是,当我试图在我的 PC 上本地运行一个项目以进行调试时,这些链接的文件不起作用。我得到一个未找到的文件。

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

谢谢,

4

4 回答 4

35

此问题的解决方案是复制在每次构建期间作为链接添加的内容文件(如 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 目标,它将在构建期间作为链接添加到项目文件夹的所有内容文件复制。

于 2012-12-13T20:50:10.887 回答
33

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

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

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

于 2013-05-10T00:37:44.543 回答
7

也可以通过将以下代码段粘贴到包含链接文件的 .proj 文件的末尾来解决此问题

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

于 2016-04-06T21:28:02.173 回答
0

将链接文件复制到虚拟位置存在一个大问题:如果您在指向其中一个链接文件的 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

全球.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 语言一样简单的跨项目引用,但可能已经有一些解决方法来启用此类功能)。

请让我知道它是否适合您,或者您是否有更好的方法。

于 2014-06-03T13:18:53.473 回答