为每个生产环境发布一个带有 web.config 的 MVC 应用程序

Publish an MVC application with a web.config for each production environment

我有一个带有 DevStagingProduction 环境的 MVC 应用程序。 Dev 和 Staging 本质上是同一件事(相同的 VM、IIS、DB 等);但是,生产托管在负载均衡器后面的 4 个虚拟机上。每个虚拟机都有自己的数据库。例如部署到VM1的实例与PROD1 DB通信,VM2->PROD2等

为了部署到 Dev 和 Staging,我使用 Debug/Release web.config 转换从 VS2013 到 VM 进行了简单的文件系统部署。对于生产部署,系统管理员会将在暂存中部署和测试的位复制到每个生产 VM。这是为了确保在暂存中由 QA 测试和验证的是我们推广到生产的内容——我不想在暂存和生产之间进行另一个构建。因此,我们的系统管理员负责(在 DevOps 指导下)编辑暂存和生产之间的每个 web.config。这基本上包括将 connectionString 值从 "Data Source=STAGINGDB" 更改为 "Data Source=PROD1"(以及 PROD2、PROD3、PROD4)。

我最终想要的是当我发布到 Staging 时,我想使用标准 Release web.config 转换来部署我的 web.config;但是,除此文件外,我还想创建和删除 4 个附加文件(web.config.PROD1、.PROD2 等)。这将允许我们创建忽略现有 web.config(使用暂存设置)和 copy/rename 适当的 .PROD 配置的脚本。

我能够(某种程度上)使用 MSBuild 实现此目的:

<Project ToolsVersion="12.0" DefaultTargets="Build">
    ...
    <Target Name="Build">
        <TransformXml Source="Web.config" Transform="Web.PROD1.config" Destination="Web.config.PROD1" />
        <TransformXml Source="Web.config" Transform="Web.PROD2.config" Destination="Web.config.PROD2" />
        ...
    </Target>
</Project>

我对这种方法的主要问题是我必须创建 4 个基本上冗余的解决方案配置来连接到转换。除 DB connectionString 外,每个设置都相同。看来应该有更有效的方法。

我可以在没有解决方案配置的情况下通过简单地通过 MSBuild 调用适当的转换来执行单独的转换,例如:

<add name="connectionString" connectionString="PROD1" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />

我应该完全使用另一个进程吗?如果我可以远离它,我宁愿不使用第 3 方 nuget 解决方案。我应该使用 .wpp.targets 文件吗? XmlPoke?

我想要的工作流程

  1. 右键单击我的 MVC 应用程序并选择 "Publish"(文件系统)
  2. 让 Release 转换完成它们的工作并生成 web.config。我有基本配置。调试 = 开发,发布 = 暂存。

  1. 添加生成 4 个额外 web.config 文件的自定义步骤
  2. 打包所有东西,发布到登台服务器,所以我在 VM 上看到了这个:

我阅读的所有内容都让我相信我应该编写自定义 MSBuild 步骤,但我不知道我应该做什么(或如何做)。这是一些伪代码:

<Project ToolsVersion="12.0" DefaultTargets="Build">
    ...
    <Target Name="Build">
        <TransformXml Source="Web.config" Transform="[Do-Basic-Transform-On-Conection-String]" Destination="Web.config.PROD1" />
        <TransformXml Source="Web.config" Transform="[Do-Basic-Transform-On-Conection-String]" Destination="Web.config.PROD2" />
        <IncludeFilesInPublish>
            <FileToInclude>Web.config.PROD1</FileToInclude>
            <FileToInclude>Web.config.PROD2</FileToInclude>
        </IncludeFilesInPublish>
    </Target>
</Project>

我认为您的问题是双重的:1) 如何将环境变量或参数(即 PROD1)传递到我的 xdt 转换文件中以便我只需要使用一个转换文件? 和 2) 我如何让 MSBuild 迭代一组已知的命名项以生成由该集合中的每个项区分的输出?

对于第一部分,我断言你可能会问这个的唯一原因是因为你说 "I have to create 4 essentially redundant solution configurations to wire up to the Transform",所以如果你的转换采用 "PROD1" 作为参数,你最好只使用一个转变。但我不确定您是否可以在不创建自己的 XmlTransform 任务的情况下执行此操作。 xdt 转换工具真的很有限。但 MSBuild 的优点在于它足够灵活,理论上您可以想出自己的转换任务,extends/subclasses 或表现得像开箱即用的任务。

using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Web.Publishing.Tasks;

namespace Thanks.IllWriteMyOwnTasks
{
    public class MyCustomTransformXml : TransformXml // no idea if you can do this
    {
        public override bool Execute()
        {
            // do stuff here, 
            // maybe declare parameters that you can pass down to base.Execute()
            return true;
        }
    }
}

..

<!--<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v11.0\Web\Microsoft.Web.Publishing.Tasks.dll" />-->
<UsingTask TaskName="MyCustomTransformXml" AssemblyFile="Thanks.IllWriteMyOwnTasks.dll" />

对于第二部分,我认为你可以使用 ItemGroup。

<ItemGroup>
    <MyEnvironments Include="PROD1" />
    <MyEnvironments Include="PROD2" />
    <MyEnvironments Include="PROD3" />
    <MyEnvironments Include="PROD4" />
</ItemGroup>  
<Target Name="BeforeBuild">
    <TransformXml Source="Web.config" 
                  Transform="Web.%(MyEnvironments.Identity).config" 
                  Destination="Web.config.%(MyEnvironments.Identity)" />
</Target>

我还没有测试过这个,但是我 认为 根据我所看到的 over here 它会在迭代时自动重复相同的任务 MyEnvironments 在这个例子中。

您可以在不添加新的解决方案配置的情况下将额外的转换文件添加到您的解决方案中,并且 运行 转换如您自己的示例(目标除外。'Build' 无效对我来说):

<Project ToolsVersion="12.0" DefaultTargets="Build">
    ...
    <Target Name="BeforeBuild">
        <TransformXml Source="Web.config" Transform="Web.PROD1.config" Destination="Web.config.PROD1" />
        <TransformXml Source="Web.config" Transform="Web.PROD2.config" Destination="Web.config.PROD2" />
        ...
    </Target>
</Project>

如果您只是更改连接字符串,您的转换文件将非常小:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
    <connectionStrings>
      <add name="MyDB" 
        connectionString="Data Source=PROD1SQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True" 
        xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
    </connectionStrings>
</configuration>

添加上述内容并编译后,您需要将新生成的 Web.config.PRODX 文件添加到您的解决方案中。添加它们之后,您只需打开每个文件的属性并确保它们的编译操作设置为 'Content'。这意味着它们包含在您的部署中。

由于 web.PRODX.config 转换文件不是解决方案配置的一部分,您可以将它们放在一个文件夹中以减少混乱。