在 Visual Studio 中进行构建时进行配置自定义






4.93/5 (10投票s)
在编译时修改具有特定于机器或生成的配置文件。
引言
本文介绍了一种解决方案,用于解决多个开发人员和环境的配置文件管理的长期问题。核心是一个命令行工具,该工具将基本(默认)配置文件与一个截断的文件(差异或“diff”文件)合并。此 diff 文件仅包含需要添加或更改的元素。
问题
你懂的。两名、十名或二十名开发人员拥有 Web 和/或应用程序配置文件的可写本地副本。QA、暂存和生产服务器也有自己的副本。必须将更改传播到所有版本,其中一些版本可能不在源控件中。各种变通方法被实施——有时,在同一个项目上实施不止一种。
电子邮件满天飞。开发人员和测试人员正在浪费时间,追踪假性错误,因为有人没有更新配置文件。
解决方案
基于机器名或生成配置,将基本配置文件与“差异”文件合并。采用此技术,无需本地维护文件;所有文件都可以签入源控件,并且每个人的文件结构都相同。
这是示例中的 app.config,它演示了合并可以处理的一些不同场景
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="ApplicationConnString"
connectionString="Data Source=OtherBox; ... ;User ID=not_sa;Password=abc"
providerName="System.Data.SqlClient" />
</connectionStrings>
<appSettings>
<add key="GeneralSettingOne" value="ValueOne" />
<add key="GeneralSettingTwo" value="ValueTwo" />
</appSettings>
<system.web>
<httpHandlers>
<add verb="*" path="*.example" type="ExampleHandler" />
<add verb="GET,HEAD" path="*.specific" type="SpecificHandler"/>
</httpHandlers>
</system.web>
...
Pages and pages of other stuff
...
</configuration>
现在,假设一个开发人员想自定义此文件的多个方面。她需要修改连接字符串,修改一个 appSetting
,并添加一个新的 appSetting
。她还将添加一个全新的诊断部分,用于跟踪。diff 文件 app.devbox1.config 可能看起来像这样
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="ApplicationConnString"
connectionString="Data Source=MyBox; ... ;User ID=not_sa;Password=abc"
providerName="System.Data.SqlClient" />
</connectionStrings>
<appSettings>
<add key="GeneralSettingTwo" value="MyGeneralSettingValue" />
<add key="MySetting" value="MyValue" />
</appSettings>
<system.diagnostics>
<sources>
...
</sources>
<sharedListeners>
...
</sharedListeners>
</system.diagnostics>
</configuration>
此文件仅包含要添加或更改的元素,而不是将保持不变的其他内容的页面。在生成后事件期间,diff 文件会被合并到源文件中并放置在输出文件夹中。
diff 文件中具有与基本文件中的 name
或 key
属性匹配的元素会覆盖基本文件值。因此,ApplicationConnString
和 GeneralSettingTwo
会被覆盖。
如果 name
或 key
是新的,则会将该元素添加到基本文件中,因此会添加 MySetting
。
如果基本文件树中没有对应的元素,则会添加 diff 文件中的元素及其子元素。因此,整个 <system.diagnostics>
部分将被添加到目标文件中。
工作原理
postbuild 命令指定了基本、diff 和目标配置文件。它调用 ConfigMerge.bat,后者又调用 ConfigMerge.exe 实用程序。命令行看起来像这样
"$(SolutionDir)ConfigMerge.bat" "$(ProjectDir)"
app.config "$(OutDir)$(TargetFileName).config"
app.%COMPUTERNAME%.config "app.$(ConfigurationName).config"
批处理文件和实用程序文件可以安全地用于包含空格的路径;这就是命令行中所有引号的原因。让我们检查一下每个元素
$(SolutionDir)ConfigMerge.bat
- 批处理文件路径。放置在解决方案根目录中,可用于多个项目。$(ProjectDir)
- 项目根目录。用于构造文件路径并查找 ConfigMerge.exe 文件,该文件假定在批处理中位于解决方案根目录。app.config
- 基本配置文件名(对于 Web 项目,请更改为 web.config)。$(OutDir)$(TargetFileName).config
- 目标文件名。如果它与基本文件相同,将被覆盖。app.%COMPUTERNAME%.config
- 要查找的第一个 diff 文件;在这种情况下,文件名为执行生成的计算机名称。对于将在本地运行的开发人员生成很有用。app.$(ConfigurationName).config
- 可选的第二个要查找的文件;在这种情况下,文件名为生成配置名称。对于将在生成服务器上运行的生成很有用。
批处理文件没有什么特别之处;它会检查参数并调用合并实用程序。
IF EXIST "%_diff%" GOTO DOMERGE
:: Optional second file to do a diff against, if it exists
IF NOT [%~5]==[] (
SET _diff=%_path%%~5
IF EXIST "%_diff%" GOTO DOMERGE
)
如果未找到任何 diff 文件,它只会将基本文件复制到目标文件。
:DOMERGE
:: Modify the config with the difference file
"%_path%..\ConfigMerge.exe" "%_config%" "%_diff%" "%_target%"
:: If ConfigMerge.exe reports an exception, pass this up to visual studio
IF ERRORLEVEL 1 EXIT 1
在此过程中,Visual Studio 的输出窗口会显示有关合并进度和正在执行的操作的消息。ConfigMerge.exe 中的异常将导致生成失败。
Compile complete -- 0 errors, 0 warnings
Test Application -> C:\Source\ConfigMerge\Test Application\bin\Debug\Tes...
"C:\Source\ConfigMerge\ConfigMerge.bat" "C:\Source\ConfigMerge\Test Appl...
ConfigMerge: Modifying [C:\Source\ConfigMerge\Test Application\app.confi...
ConfigMerge: Wrote target file [C:\Source\ConfigMerge\Test Application\b...
========== Build: 2 succeeded or up-to-date, 0 failed, 0 skipped =========
ConfigMerge.exe 控制台应用程序也相当简单。由于它依赖于特定的属性,因此不适合通用的 XML 合并,但对于 .NET 配置文件来说效果很好。
ConfigMerge.exe 递归处理 diff XML 文档中的节点
static void ProcessNode(XmlNode baseNode, XmlNode diffNode)
{
// Check each of the children to see if they match
foreach (XmlNode diffNodeChild in diffNode.ChildNodes)
{
. . .
// Look for corresonding nodes in the base document.
// If the child node has a name, key, or other recognized set of
// attributes, we'll look for a corresponding node based on that
bool namedPath = false;
string path = GetComparisonPath(diffNodeChild, out namedPath);
XmlNodeList children = baseNode.SelectNodes(path);
// Does the base document have corresponding nodes?
. . .
if (children.Count == 1)
{
// Replace the node if it is recognized (update with new
// information) or if it is an "endpoint" node.
// For endpoints, it is assumed that it wouldn't be in the diff
// file if it wasn't different somehow, even if there is no
// name attribute.
if (namedPath || !diffNodeChild.HasChildNodes)
{
XmlNode newNode = baseNode.OwnerDocument.ImportNode(
diffNodeChild, true);
baseNode.ReplaceChild(newNode, children[0]);
}
else
{
// Node is not named, and has children; process the
// children looking for differences
ProcessNode(children[0], diffNodeChild);
}
}
else
{
// No corresponding node; stick this whole node in there
XmlNode newNode = baseNode.OwnerDocument.ImportNode(
diffNodeChild, true);
baseNode.AppendChild(newNode);
}
}
}
匹配是基于一组识别的参数完成的。目前,这包括以下内容
static string[] UniqueAttributes = new string[] {
"name",
"key",
"verb,path" }; // No space after comma!
verb
和 path
用于 HttpHandler
。如果需要其他一组属性,可以将其添加到此数组中。
集成到您的项目中
这部分很简单。只要将 ConfigMerge 批处理和控制台应用程序放在解决方案根目录中,您就可以在不修改的情况下使用批处理文件和 postbuild 命令行示例。将文件放到解决方案的根目录中,将它们添加到项目中(它们会在解决方案资源管理器下的“解决方案项”中显示),然后将 postbuild 命令行复制到您的项目中。
现在,将您的本地 app.config 复制到 app.mycomputername.config,从其中剪切所有未修改的部分,然后获取最新版本。生成。您就完成了。其他团队成员可以复制现有的 diff 文件,重命名它,并为他们的本地机器进行修改。
对于使用 web.config 文件的 ASP.NET 或 WCF 应用程序,存在一个小的复杂性,即配置文件不会传输到输出目录。
合并 web.config 文件有两种选择
- 创建一个具有不同名称的模板文件,并将合并的目标设置为 web.config。如果未检测到 diff 文件,批处理文件会复制基本文件,因此 web.config 总是会被创建。
"$(SolutionDir)ConfigMerge.bat" "$(ProjectDir)" web.template.config web.config web.%COMPUTERNAME%.config "web.$(ConfigurationName).config"
- 在 postbuild 命令行中将基本和目标都设置为 web.config,这将导致合并覆盖现有的 web.config。下次从源控件获取时它可能会被覆盖,但会由生成重新创建。
"$(SolutionDir)ConfigMerge.bat" "$(ProjectDir)" web.config web.config web.%COMPUTERNAME%.config "web.$(ConfigurationName).config"
示例项目
可下载的 Visual Studio 2005 解决方案包括带有源代码的 ConfigMerge 项目,以及 ConfigMerge.bat 文件。它还包括一个带有配置文件的示例应用程序项目,已为此项目配置了 postbuild。
要查看效果,请将 app.devbox1.config 重命名为您的计算机名称,或重命名为 app.debug.config(当然,用于调试生成)。
该项目应能毫无问题地升级到 Visual Studio 2008。
历史
- 2008.06.22 - 首次发布。