.NET配置(app.config / web.config / settings.settings)


162

我有一个.NET应用程序,它具有用于调试和发布版本的不同配置文件。例如,debug app.config文件指向已启用调试的开发SQL Server,发布目标指向实时SQL Server。还有其他设置,其中一些在调试/发行版中有所不同。

我目前使用两个单独的配置文件(debug.app.config和release.app.config)。我在项目上有一个构建事件,它说如果这是发行版本,则将release.app.config复制到app.config,否则将debug.app.config复制到app.config。

问题是该应用程序似乎是从settings.settings文件中获取其设置的,所以我必须在Visual Studio中打开settings.settings,然后提示我设置已更改,因此我接受更改,保存settings.settings并具有重建以使其使用正确的设置。

是否有更好/推荐/首选的方法来达到类似的效果?或者同样,我是否完全解决了这个错误,还有更好的方法吗?


我想在Windows中禁用调试的,我已经通过取消所有调试设置复选框试过,但我仍然可以调试斌发布的exe ..任何人帮我在这..
Vinoth纳拉

Answers:


62

跨环境的任何配置都应存储在计算机级别,而不是应用程序级别(有关配置级别的更多信息。)

这些是我通常在计算机级别存储的配置元素:

当每个环境(开发人员,集成,测试,阶段,实时)在c:\ Windows \ Microsoft.NET \ Framework64 \ v2.0.50727 \ CONFIG目录中都有自己的唯一设置时,则可以在环境之间升级应用程序代码而无需任何操作构建后的修改。

显然,计算机级别CONFIG目录的内容在应用程序所在的其他存储库或文件夹结构中受版本控制。您可以通过智能使用configSource来使.config文件对源代码的控制更加友好。

我已经在超过25个不同公司的200多个ASP.NET应用程序上进行了7年的研究。(不是要吹嘘,只是想让你知道,我从来没有见过的情况下这种做法工作。)


3
如果您无法控制Web服务器,因此无法更改计算机级别的配置,该怎么办?示例包括第三方Web服务器或企业中多个部门之间共享的Web服务器。
RationalGeek 2010年

1
不行 但是,在虚拟机时代,Amazon EC2和戴尔提供的$ 400服务器中,有人真的对共享计算机做任何严肃的事情吗?一点也不试图变得冷酷无情-我真的认为,如果您正在使用共享Web服务器,则应该重新评估。
波特曼2010年

7
我与内部站点合作的大多数公司都在一台服务器上托管多个应用程序-必须在公司级别进行重新评估
MPritchard 2010年

一台服务器上的多个应用程序都可以,只要这些应用程序都在同一个“环境”中即可。即,您不希望App1的LIVE实例与App2的DEV实例位于同一服务器上。例如,您的SMTP设置将在所有应用程序之间共享。在生产中,您指向真实的邮件服务器;在开发中,您指向磁盘上的文件。
波特曼

7
我知道这可以工作,但这仍然与我尝试自动化部署时的建议背道而驰。我认为设置是特定于应用程序的,它们需要与应用程序一起进行版本控制,并与之一起发展。依靠机器配置只是在变化,我认为这使它变得更加困难。我喜欢将变化的事物放在一起,并一起部署。如果为Dev添加新设置,则可能需要与prod等效的设置。
米格尔·马德罗

51

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

如果将GenerateDefaultValueInCode设置为True(此处为默认值,则为True!),则将默认值编译到EXE(或DLL)中,当您在纯文本编辑器中将其打开时,可以将其嵌入文件中。

我正在使用控制台应用程序,如果我在EXE中默认设置,则该应用程序将始终忽略位于同一目录中的配置文件!一场噩梦,整个互联网上对此一无所知。


7
这正是我过去一个周末发生的事情。我花了很多时间试图弄清楚为什么我的应用程序似乎忽略了我的app.config文件!它应该连接到Web服务,并且服务URL在我的app.config中。对我而言,我不知道的是,当我创建Web参考时,它还创建了一个Settings.Settings文件,并将默认值硬编码到代码中。即使最终确定(并删除了)设置文件,该默认值仍保留在硬代码中并嵌入到exe中。非常烦恼!感谢这篇文章,现在我可以摆脱这个“功能”了
Mike K

+1这个答案很关键:如果希望您的设置进入app.config文件,请将其GenerateDefaultValueInCode属性设置为False(默认值为True)。
Sabuncu

34

这里有一个相关的问题:

改善构建过程

配置文件带有一种覆盖设置的方法:

<appSettings file="Local.config">

无需检入两个(或多个)文件,而仅检入默认配置文件,然后在每台目标计算机上,放置一个Local.config,其中只有appSettings部分具有该特定计算机的替代项。

如果使用配置节,则等效项为:

configSource="Local.config"

当然,最好从其他计算机制作所有Local.config文件的备份副本,并在某个位置检入它们,但不作为实际解决方案的一部分。每个开发人员都会在Local.config文件上放置一个“忽略”,以便它不会被检入,这将覆盖其他所有人的文件。

(您实际上不必将其称为“ Local.config”,这就是我所使用的)


14

从我阅读的内容来看,听起来好像您在使用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>


8

我们曾经使用Web部署项目,但此后已迁移到NAnt。当前我们没有分支和复制不同的设置文件,而是将配置值直接嵌入到构建脚本中,然后通过xmlpoke任务将它们注入到我们的配置文件中:

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

无论哪种情况,您的配置文件都可以具有您想要的任何开发人员值,并且它们可以在您的开发环境中正常工作而不会破坏您的生产系统。我们发现,开发人员在进行测试时不太可能随意更改构建脚本变量,因此,与我们尝试过的其他技术相比,偶然的错误配置很少见,尽管仍然有必要在过程中尽早添加每个var,以便默认情况下,dev值不会推送到prod。


7

我现在的老板通过首先将dev级别(调试,阶段,实时等)放置在machine.config文件中来解决此问题。然后他们编写了代码以进行选择并使用正确的配置文件。部署应用后,解决了连接字符串错误的问题。

他们最近才编写了一个中央Web服务,该服务从machine.config值中的值发送回正确的连接字符串。

这是最好的解决方案吗?可能不是,但是对他们有用。


1
实际上,我认为这很不错,因为我喜欢在解决方案中保持各种版本的config可见,即使它们不存在。
annakata

1
这是一个非常有趣的解决方案。很乐意查看实际操作中的示例。
Mike K'3

5

最有效的解决方案之一是使用WebDeploymentProject。在我的站点中,我有2/3个不同的web.config文件,在发布时,根据所选的配置模式(发行/登台/等...),我将在Web.Release.config上进行复制并将其重命名为Web。在AfterBuild事件中进行配置,并删除不需要的内容(例如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>


3

我们的项目存在相同的问题,我们必须维护dev,qa,uat和prod的配置。这是我们遵循的内容(仅在您熟悉MSBuild的情况下适用):

将MSBuild与MSBuild社区任务扩展一起使用。它包括“ XmlMassUpdate”任务,一旦您为其指定了正确的节点,它便可以“批量更新”任何XML文件中的条目。

实施:

1)您需要一个配置文件,其中将包含您的dev env条目;这是您解决方案中的配置文件。

2)您需要有一个'Substitutions.xml'文件,该文件仅包含每个环境的不同条目(主要是appSettings和ConnectionStrings)。在整个环境中不变的条目不需要放在此文件中。它们可以存在于解决方案的web.config文件中,而不会被任务触及

3)在构建文件中,只需调用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>

根据环境,将“ $ Environment”替换为“ QA”或“ Prod”。您正在为。请注意,您应该处理配置文件的副本,而不要处理实际的配置文件本身,以避免任何可能的不可恢复的错误。

只需运行构建文件,然后将更新的配置文件移动到部署环境即可!

有关更好的概述,请阅读以下内容:

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


2

像您一样,我还设置了“多” app.config-例如,app.configDEV,app.configTEST,app.config.LOCAL。我看到了一些不错的建议,但是,如果您喜欢它的工作方式,请添加以下内容:


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

我只是将配置重命名为我要定位的配置(我有一个包含8个应用程序的项目,其中有许多数据库/ wcf配置针对4个事件)。为了通过clickonce进行部署,我更改了项目中的4个步骤。(这我想自动化)

我唯一的陷阱是记住更改后要“清除所有内容”,因为手动重命名后旧配置已被“卡住”。(我认为这将解决您的setting.setting问题)。

我发现这真的很好用(有一天我会花时间看一下MSBuild / NAnt)


0

Web.config:

要在IIS上承载应用程序时需要Web.config。Web.config是IIS的必需配置文件,用于配置它在Kestrel之前作为反向代理的行为。如果要在IIS上托管web.config,则必须对其进行维护。

AppSetting.json:

对于与IIS不相关的其他所有内容,请使用AppSetting.json。AppSetting.json用于Asp.Net Core托管。ASP.NET Core使用“ ASPNETCORE_ENVIRONMENT”环境变量来确定当前环境。默认情况下,如果您在不设置此值的情况下运行应用程序,它将自动默认为生产环境并使用“ AppSetting.production.json”文件。通过Visual Studio调试时,它将环境设置为“开发”,因此它使用“ AppSetting.json”。请访问此网站以了解如何在Windows上设置托管环境变量。

App.config:

App.config是.NET使用的另一个配置文件,主要用于Windows窗体,Windows服务,控制台应用程序和WPF应用程序。当您通过控制台应用程序app.config启动Asp.Net Core托管时。


TL; DR

配置文件的选择取决于您为服务选择的托管环境。如果使用IIS承载服务,请使用Web.config文件。如果使用任何其他托管环境,请使用App.config文件。请参阅使用配置文件配置服务文档,在ASP.NET Core中签出配置。


0

上面说的是asp.net,那么为什么不将设置保存在数据库中并使用自定义缓存来检索它们呢?

我们这样做的原因是,(对于我们而言)更新连续数据库要比获得连续更新生产文件的权限容易。

自定义缓存的示例:

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
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.