.NETconfiguration(app.config / web.config / settings.settings)

我有一个.NET应用程序,具有不同的debugging和发布版本的configuration文件。 例如,debuggingapp.config文件指向启用了debugging的开发SQL Server ,并且发布目标指向实时SQL Server。 还有其他一些设置,其中一些在debugging/发布中是不同的。

我目前使用两个单独的configuration文件(debug.app.config和release.app.config)。 我有一个项目上的生成事件,说如果这是一个发布版本,然后将release.app.config复制到app.config,否则将debug.app.config复制到app.config。

问题是,应用程序似乎从settings.settings文件得到它的设置,所以我必须在Visual Studio中打开settings.settings然后提示我,设置已经改变,所以我接受更改,保存settings.settings和重build,使其使用正确的设置。

有一个更好的/推荐/首选的方法来实现类似的效果? 或者说,我认为这是完全错误的,有没有更好的办法?

环境中可能不同的任何configuration都应该存储在机器级别 ,而不是应用程序级别 。 (有关configuration级别的更多信息。)

这些是我通常在机器级存储的configuration元素种类:

  • 应用程序设置
  • 连接string
  • 零售=真
  • Smtp设置
  • 健康监测
  • 托pipe环境
  • 机器钥匙

当每个环境(开发人员,集成,testing,阶段,现场)在c:\ Windows \ Microsoft.NET \ Framework64 \ v2.0.50727 \ CONFIG目录中都有其独特的设置时,则可以在不使用任何环境构build后修改。

显然,机器级别的CONFIG目录的内容受版本控制在不同的存储库或与您的应用程序不同的文件夹结构。 通过智能使用configSource,可以使.config文件更易于源代码pipe理。

我已经做了7年,在超过200个ASP.NET应用程序在25多个不同的公司。 (不要试图吹嘘,只是想让你知道,我从来没有看到这种方法无效的情况。)

这可能有助于一些处理Settings.settings和App.config的人:注意在Properties窗格中的GenerateDefaultValueInCode属性,同时编辑Visual Studio中的Settings.settings网格中的任何值(在我的情况下为Visual Studio 2008)。

如果将GenerateDefaultValueInCode设置为True(True是此处的默认值),则将默认值编译到EXE(或DLL)中,在纯文本编辑器中打开文件时,可以在文件中find它。

我正在开发一个控制台应用程序,如果我在EXE中有默认值,应用程序总是忽略configuration文件放在同一个目录中! 这是一个噩梦,整个互联网上都没有关于这方面的信息。

这里有一个相关的问题:

改善你的构build过程

configuration文件有一个方法来覆盖设置:

<appSettings file="Local.config"> 

您只需检查默认的configuration文件,然后在每个目标机器上检查两个文件(或更多),而不是检查两个文件(或更多),只需要具有覆盖该特定机器的appSettings部分即可。

如果你正在使用configuration部分,相当于:

 configSource="Local.config" 

当然,从其他机器制作所有Local.config文件的备份副本并在某处进行检查是一个不错的主意,但不是作为实际解决scheme的一部分。 每个开发者在Local.config文件上放一个“忽略”,所以不会被检入,这会覆盖其他人的文件。

(你实际上不必把它称为“Local.config”,这正是我所使用的)

从我正在阅读,这听起来像你正在使用Visual Studio的生成过程。 你有没有想过使用MSBuild和Nant呢?

Nant的xml语法有点奇怪,但是一旦你理解了,你提到的就变得很微不足道了。

 <target name="build"> <property name="config.type" value="Release" /> <msbuild project="${filename}" target="Build" verbose="true" failonerror="true"> <property name="Configuration" value="${config.type}" /> </msbuild> <if test="${config.type == 'Debug'}"> <copy file=${debug.app.config}" tofile="${app.config}" /> </if> <if test="${config.type == 'Release'}"> <copy file=${release.app.config}" tofile="${app.config}" /> </if> </target> 

对我来说,你似乎可以从Visual Studio 2005 Web部署项目中受益。

有了这个,你可以告诉它根据构buildconfiguration更新/修改web.config文件的部分。

请看Scott Gu的这个博客文章,以获得快速的概述/示例。

我们过去使用Web部署项目,但已经迁移到NAnt。 我们目前将configuration值直接embedded到构build脚本中,并通过xmlpoke任务将其注入到configuration文件中,而不是分支和复制不同的设置文件:

  <xmlpoke file="${stagingTarget}/web.config" xpath="/configuration/system.web/compilation/@debug" value="true" /> 

在任何一种情况下,您的configuration文件都可以拥有您想要的任何开发人员值,并且可以在开发环境内正常工作,而不会中断您的生产系统。 我们发现开发人员在testing时不太可能随意更改构build脚本variables,所以偶然的错误configuration比我们尝试过的其他技术更为罕见,尽pipe仍然有必要在stream程的早期阶段添加每个variables,以便dev的默认值不会被推到prod上。

我目前的雇主解决了这个问题,首先把开发级别(debugging,阶段,生活等)在machine.config文件。 然后他们编写代码来select并使用正确的configuration文件。 这就解决了应用程序部署后连接string错误的问题。

他们最近写了一个中央web服务,从machine.config的值中返回正确的连接string。

这是最好的解决scheme吗? 可能不是,但它适用于他们。

其中一个很好的解决scheme是使用WebDeploymentProject。 我有2/3不同的web.config文件在我的网站,并在发布,具体取决于所选的configuration模式(释放/登台/ etc …)我将通过Web.Release.config复制并将其重命名为网站。在AfterBuild事件configuration,并删除我不需要的(例如Web.Staging.config)。

 <Target Name="AfterBuild"> <!--Web.config --> <Copy Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " SourceFiles="$(SourceWebPhysicalPath)\Web.Release.config" DestinationFiles="$(OutputPath)\Web.config" /> <Copy Condition=" '$(Configuration)|$(Platform)' == 'Staging|AnyCPU' " SourceFiles="$(SourceWebPhysicalPath)\Web.Staging.config" DestinationFiles="$(OutputPath)\Web.config" /> <!--Delete extra files --> <Delete Files="$(OutputPath)\Web.Release.config" /> <Delete Files="$(OutputPath)\Web.Staging.config" /> <Delete Files="@(ProjFiles)" /> </Target> 

您将在这里find另一个解决scheme: 在ASP.NET中的开发/ UAT / Prod环境之间切换configuration的最佳方法? 它使用XSLT来转换web.config。

使用NAnt也有一些很好的例子。

我们的proj有同样的问题,我们必须保持dev,qa,uat和prod的configuration。 以下是我们所遵循的(只有在熟悉MSBuild的情况下才适用):

与MSBuild社区任务扩展使用MSBuild。 它包括“XmlMassUpdate”任务,一旦你给它正确的节点,就可以在任何XML文件中“批量更新”条目。

实施:

1)你需要有一个configuration文件,这将有你的开发环境条目; 这是你的解决scheme中的configuration文件。

2)您需要有一个“Substitutions.xml”文件,其中只包含每个环境的不同(主要是appSettings和ConnectionStrings)条目。 不跨环境变化的条目不需要放在这个文件中。 他们可以住在解决scheme的web.config文件中,不会被任务触及

3)在您的构build文件中,只需调用XML批量更新任务,并提供适当的环境作为参数。

看下面的例子:

  <!-- Actual Config File --> <appSettings> <add key="ApplicationName" value="NameInDev"/> <add key="ThisDoesNotChange" value="Do not put in substitution file" /> </appSettings> <!-- Substitutions.xml --> <configuration xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate"> <substitutions> <QA> <appSettings> <add xmu:key="key" key="ApplicationName" value="NameInQA"/> </appSettings> </QA> <Prod> <appSettings> <add xmu:key="key" key="ApplicationName" value="NameInProd"/> </appSettings> </Prod> </substitutions> </configuration> <!-- Build.xml file--> <Target Name="UpdateConfigSections"> <XmlMassUpdate ContentFile="Path\of\copy\of\latest web.config" SubstitutionsFile="path\of\substitutionFile" ContentRoot="/configuration" SubstitutionsRoot="/configuration/substitutions/$(Environment)" /> </Target> 

用'QA'或'Prod'replace'$ Environment'。 你正在build设。 请注意,您应该在configuration文件的副本上工作,而不是实际的configuration文件本身,以避免任何可能的不可恢复的错误。

只需运行构build文件,然后将更新的configuration文件移动到您的部署环境,即可完成!

为了更好的概述,请阅读以下内容:

http://blogs.microsoft.co.il/blogs/dorony/archive/2008/01/18/easy-configuration-deployment-with-msbuild-and-the-xmlmassupdate-task.aspx

就像你我也设置了'多'app.config – 例如app.configDEV,app.configTEST,app.config.LOCAL。 我看到一些优秀的替代build议,但如果你喜欢它适合你的方式,我会添加以下内容:

我有一个
<appSettings>
<add key = "Env" value = "[Local] "/>对于每个应用程序,我将它添加到标题栏中的UI:从ConfigurationManager.AppSettings.Get(“Env”);

我只是将configuration重命名为我正在设置的configuration(我有一个项目,有8个应用程序,其中包含大量的数据库/ wcfconfiguration,对4个镜像)。 用clickonce部署到每个我在项目中更改4 seetings并去。 (这个我很想自动化)

我唯一需要考虑的是在更改之后“清理所有”,因为旧的configuration在手动重命名之后“卡住”了。 (我认为这会解决你setting.setting问题)。

我觉得这个工作真的很好(有一天我会抽空看看MSBuild / NAnt)

它说上面的asp.net,所以为什么不把你的设置保存在数据库中,并使用自定义caching来检索它们?

我们在这里做的原因是因为更新连续数据库比获取连续更新生产文件的权限更容易(对我们来说)。

自定义caching的示例:

 public enum ConfigurationSection { AppSettings } public static class Utility { #region "Common.Configuration.Configurations" private static Cache cache = System.Web.HttpRuntime.Cache; public static String GetAppSetting(String key) { return GetConfigurationValue(ConfigurationSection.AppSettings, key); } public static String GetConfigurationValue(ConfigurationSection section, String key) { Configurations config = null; if (!cache.TryGetItemFromCache<Configurations>(out config)) { config = new Configurations(); config.List(SNCLavalin.US.Common.Enumerations.ConfigurationSection.AppSettings); cache.AddToCache<Configurations>(config, DateTime.Now.AddMinutes(15)); } var result = (from record in config where record.Key == key select record).FirstOrDefault(); return (result == null) ? null : result.Value; } #endregion } namespace Common.Configuration { public class Configurations : List<Configuration> { #region CONSTRUCTORS public Configurations() : base() { initialize(); } public Configurations(int capacity) : base(capacity) { initialize(); } public Configurations(IEnumerable<Configuration> collection) : base(collection) { initialize(); } #endregion #region PROPERTIES & FIELDS private Crud _crud; // Db-Access layer #endregion #region EVENTS #endregion #region METHODS private void initialize() { _crud = new Crud(Utility.ConnectionName); } /// <summary> /// Lists one-to-many records. /// </summary> public Configurations List(ConfigurationSection section) { using (DbCommand dbCommand = _crud.Db.GetStoredProcCommand("spa_LIST_MyConfiguration")) { _crud.Db.AddInParameter(dbCommand, "@Section", DbType.String, section.ToString()); _crud.List(dbCommand, PopulateFrom); } return this; } public void PopulateFrom(DataTable table) { this.Clear(); foreach (DataRow row in table.Rows) { Configuration instance = new Configuration(); instance.PopulateFrom(row); this.Add(instance); } } #endregion } public class Configuration { #region CONSTRUCTORS public Configuration() { initialize(); } #endregion #region PROPERTIES & FIELDS private Crud _crud; public string Section { get; set; } public string Key { get; set; } public string Value { get; set; } #endregion #region EVENTS #endregion #region METHODS private void initialize() { _crud = new Crud(Utility.ConnectionName); Clear(); } public void Clear() { this.Section = ""; this.Key = ""; this.Value = ""; } public void PopulateFrom(DataRow row) { Clear(); this.Section = row["Section"].ToString(); this.Key = row["Key"].ToString(); this.Value = row["Value"].ToString(); } #endregion } }