在Visual Studio中为开发者专门创建的app.config/web.config文件

98

我们有几个.NET项目,其中将某些设置存储在配置文件中。

现在每个开发人员都会有自己的配置文件,它们略有不同(连接到本地数据库的不同连接字符串,不同的WCF端点等)

目前,我们倾向于签出app/web.config文件并修改它们以满足我们的需求。

这会导致许多问题,因为偶尔会有人提交他们自己的设置或在从TFS获取最新版本时丢失自定义配置。

您如何处理这种情况?或者您根本没有这个问题吗?


10
我支持重新开放,因为这是Visual Studio开发人员常见的问题,并且直接涉及开发人员正在使用的工具(因此有投票支持)。 - Christian Gollhardt
同意,随着我们团队规模的扩大,这是一个持续存在的问题,而且各种尝试解决它都不令人满意。 - DiskJunky
9个回答

67
我们使用了一种系统,结合了此页面上现有答案的几个方面,并参考了Scott Hanselman的建议
简而言之,我们做的是拥有一个通用的app.config / web.config文件,并将大多数特定设置保存在单独的文件中,正如其他答案所建议的那样。例如,对于我们的SMTP设置,app.config包含:
<system.net>
  <mailSettings>
    <smtp configSource="config\smtp.config" />
  </mailSettings>
</system.net>

这个文件已经在源代码控制中。然而,像这样的单独文件并没有在其中:
<?xml version="1.0" encoding="utf-8" ?>
<smtp deliveryMethod="Network">
  <network host="127.0.0.1" port="25" defaultCredentials="false" password="" userName ="" />
</smtp>

这并不是故事的结尾。那新开发者或全新的源代码安装呢?大部分配置已不再受源代码控制,手动构建所有所需的.config文件非常麻烦。我更喜欢有源代码至少可以直接编译的情况。
因此,我们在源代码控制中保留了.config文件的一个版本,命名为.config.default文件。因此,新的源代码树看起来像这样:

alt text

然而,对于开发人员来说,它们只是无意义的文本文件,对Visual Studio没有任何用处。因此,批处理文件copy_default_config.bat负责从.config.default文件创建初始一组.config文件:

@echo off
@REM Makes copies of all .default files without the .default extension, only if it doesn't already exist. Does the same recursively through all child folders.
for /r %%f in (*.default) do (
    if not exist "%%~pnf" (echo Copying %%~pnf.default to %%~pnf & copy "%%f" "%%~pnf" /y)
)
echo Done.

这个脚本可以安全地重新运行,因为已经拥有 .config 文件的开发人员不会被覆盖。因此,可以将此批处理文件作为预构建事件运行。.default 文件中的值可能对于新安装来说并不完全正确,但它们是一个合理的起点。
最终,每个开发人员得到的配置文件文件夹看起来像这样:

alt text

这可能看起来有点复杂,但绝对比开发人员互相干扰的麻烦要好。


为什么不直接在存储库中放置主要的 .config 文件而是单独放置它们呢? - graffic
6
真烦人,我们需要一个更好的解决方案。我曾经有过类似的方法,但它太脆弱了,而且新开发人员还需要解释。 - jpierson
@Gavin,如果我尝试运行你所概述的bat文件,会出现错误:'∩╗┐@echo'不被识别为内部或外部命令、可操作的程序或批处理文件。 - Austin
2
@Austin,你似乎在“@echo”之前有一些不可打印的垃圾字符,其中一部分可能是Unicode字节顺序标记?也许复制/粘贴出了问题,或者批处理文件保存在无法正确读取的编码中。 - Gavin

23
在您的Web.config中使用其他文件的源。
<configuration>
    <connectionStrings configSource="ConnectionStrings.config" />
...
</configuration>

将web.config纳入版本控制,而不是ConnectionStrings.config文件。 现在所有开发人员都有一个连接字符串文件。

你可以为所有本地相关的设置进行此操作。


1
这对我很有效。参见:http://davidgiard.com/2012/05/25/MovingConfigSectionsToExternalFiles.aspx ConnectionStrings.config文件必须与应用程序或Web配置文件位于同一文件夹中。另外,您必须将其属性更改为“仅在新建或较新版本时复制”以输出。ConnectionStrings.config文件需要带有开放和关闭元素的完整部分。您还必须设置版本控制以忽略该文件,以便每个人都可以使用自己的文件。 - Jeffrey Roughgarden
1
我使用了<appSettings file="..">选项,因为我需要合并设置。 - Spikolynn
1
如果你在解决方案资源管理器中包含了“ConnectionStrings.config”文件,那么该文件不必存在于文件系统中吗?如果是这样的话,这意味着你已经将其加入到源代码控制中,而这正是我们试图摆脱的初始问题。 - Jez

20
这里提供一个关于web.config文件和Visual Studio 2010的解决方案:
1)手动编辑你的Web应用程序.csproj文件,添加一个像下面这样的AfterBuild目标:
  <Project>
   ...
    <Target Name="AfterBuild">
      <Copy SourceFiles="web.config" DestinationFiles="obj\$(Configuration)\tempweb.config" />
      <TransformXml Source="obj\$(Configuration)\tempweb.config"
                  Transform="web.$(USERNAME).config"
                  Destination="obj\$(Configuration)\tempweb2.config" />
      <ReadLinesFromFile File="obj\$(Configuration)\tempweb2.config"><Output TaskParameter="Lines" ItemName="TransformedWebConfig"/></ReadLinesFromFile>
      <ReadLinesFromFile File="web.config"><Output TaskParameter="Lines" ItemName="UnTransformedWebConfig"/></ReadLinesFromFile>
      <Copy Condition=" @(UnTransformedWebConfig) != @(TransformedWebConfig) " SourceFiles="obj\$(Configuration)\tempweb2.config" DestinationFiles="web.config" OverwriteReadOnlyFiles="True" />
    </Target>
  </Project>

这个目标会将当前开发人员对应的Web.config文件进行转换 - 因此包含$(USERNAME)变量 - 并且创建一个与1)相对应的文件。每次构建时,它只会替换本地Web.config文件,如果内容已更改(以避免重新启动),即使本地Web.config受源代码控制,也会如此,这就是为什么设置OverwriteReadOnlyFiles为True。实际上,这一点是有争议的。
2)为项目中的每个开发人员创建一个名为Web.[developer windows login].config的文件。(例如,在以下截图中,我有两个名为smo和smo2的开发人员)。

enter image description here

这些文件(每个开发者一个)可以/应该被源代码控制。它们不应该作为主Web.config的依赖项标记,因为我们想能够单独检查它们。每个文件代表要应用于主Web.Config文件的转换。转换语法在此处描述:Web应用程序项目部署的Web.config转换语法。我们重用了这个出色的Xml文件转换任务,该任务是Visual Studio自带的。任务的目的是合并Xml元素和属性,而不是覆盖整个文件。例如,这里是一个示例web.[dev login].config,它将更改名为'MyDB'的连接字符串,而不管Web.config文件的其余部分如何:
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
    <connectionStrings>
      <add name="MyDB" 
        connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True" 
        xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
    </connectionStrings>
</configuration>

现在,这种解决方案并不完美,因为:
  • 构建后,开发人员的本地Web.Config可能与源代码管理系统中的不同
  • 他们可能需要强制从源代码管理系统获取新的Web.Config时进行本地写入
  • 开发人员不应该签出/签入主要的web.config。它应该保留给少数人使用。
但至少,您只需要维护一个唯一的主Web.config加上每个开发人员的一个转换文件。
可以采用类似的方法处理App.config(而不是Web)文件,但我没有进一步阐述。

我将我的Web.config重命名为Web.base.config,创建了一个新的Web.config文件,然后在步骤1中从<Copy SourceFiles="web.config">更改为<Copy SourceFiles="web.base.config">。通过这样做,我可以拥有一个本地的web.config文件,可以在源代码控制中被忽略。 - S. Baggy
这仍然不是理想的解决方案,但我更喜欢它而不是其他的解决方案。 - JMK
你认为在web.$(USERNAME).config不存在的情况下,使用web.default.config是否可行?顺便说一句,感谢您提供这个很棒的答案。 - Christian Gollhardt

2
我们正在使用machine.config来避免在不同环境中出现web.config的差异。

14
更希望避免改变 machine.config。 - twarz01

0

忽略文件怎么样?这样就不会被检查进去了。我遇到过类似的问题,已经将 web.config 添加到 Subversion 的忽略列表中。

但在 TFS 中,稍微有点难度,请参考this post中的方法。


0

忽略那些文件,使用一个 Commom_Web.Config 和 Common_App.Config 文件。使用一个持续集成的构建服务器来执行构建任务,并将这两个文件重命名为普通名称,以便构建服务器可以正常工作。


这需要在开发机器上完成,然后再传到构建服务器。 - twarz01
不应该在开发者本地重命名文件,这个操作应该在构建服务器上进行。常用的配置文件存储在Subversion中,它们与特定的开发人员无关。虽然每个开发者都运行自己的个人配置文件来在自己的机器上进行开发,但他们不必提交这些文件,因为它们不是Subversion的一部分。 - Arthis
这个方案不支持开发人员调试应用程序。在调试期间,源文件夹中的根web.config被使用。例如,如果您将web.config添加到.gitignore并要求用户将Commom_Web.Config复制到web.config并按照他们的喜好进行编辑,则已经完成了部分工作。但是,当您需要编辑所有开发人员机器上相同的内容时,例如system.web/compilation部分或特定的appSettings,此方案就会崩溃。 - binki

0
我们有相同的问题,我们正在做的是:
  • 在 web.config 中检查测试服务器/生产值
  • 开发者进入 Windows 文件资源管理器并更改文件的只读模式
  • 编辑配置以适应其环境。
  • 只有在部署值更改或添加或删除任何配置条目时才会在 web.config 中进行检查。
  • 这需要为每个配置条目提供大量注释,因为每个开发人员都需要更改它们

1
这在使用默认设置将字段设置为只读的源代码控制的商店中可能效果更好,但对于使用Subversion的其他人来说可能效果不佳。我们可以在存储库中设置只读权限以防止其被提交,但这需要针对每个分支的每个配置文件进行设置。 - jpierson
对于我们这些没有使用诸如TFS之类的工具的人来说,这个解决方案如何运作并不明显。你能否再给一些背景信息呢?此外,它不需要开发人员小心谨慎地避免意外检出文件吗? - binki

0

你可以应对的一种方式是拥有一个标记化的系统,并使用rake脚本更改值。

一种更为基础的方法可以是在web.config中为所有AppSettings(类似于连接)提供指向AppSettings.config文件的链接。

<appSettings configSource="_configs/AppSettings.config" />

然后,每个开发人员都有一个子文件夹版本的文件夹(即/_configs/dave/)。当开发人员在自己的代码上工作时,他们将从子文件夹复制到链接文件夹的根目录。

您需要确保向这些文件通信更改(除非您进行令牌化)。如果将AppSettings.config文件保留在源代码控制之外,并仅检查开发人员的个人文件夹(所有文件夹),则他们将被迫复制正确的文件。

我更喜欢令牌化,但如果这只是一个快速修复,那么可能会更难以启动和运行。


-1
假设您正在使用Visual Studio,为什么不使用不同的解决方案配置呢? 例如:您可以拥有一个使用Web.config.debug的调试配置和一个使用Web.config.release的发布配置。Web.config.debug应该类似于:

<appSettings file="C:/Standard_Path_To_Configs/Standard_Name_For_Config.config"> 

有一个名为Standard_Name_For_Config.config的文件,其中包含所有个人开发者设置,而Web.config.release则始终具有生产设置。 您可以将默认配置存储在某些源控制文件夹中,并让新用户从那里获取它们。


然后,每个将对项目进行贡献的个人用户都需要拥有自己的构建配置。这似乎无法扩展,并且不支持接受贡献的项目。 - binki

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