【问题标题】:Does ConfigurationManager.AppSettings[Key] read from the web.config file each time?ConfigurationManager.AppSettings[Key] 是否每次都从 web.config 文件中读取?
【发布时间】:2012-11-01 16:56:49
【问题描述】:

我想知道ConfigurationManager.AppSettings[Key] 是如何工作的。

每次我需要密钥时它都会从物理文件中读取吗?如果是这样,我是否应该在缓存中读取我的 web.config 的所有应用设置,然后从中读取?

还是 ASP.NET 或 IIS 在应用程序启动时只加载一次 web.config 文件?

如何验证每次读取是否访问了物理文件?如果我更改 web.config,IIS 会重新启动我的应用程序,因此我无法以这种方式对其进行验证。

【问题讨论】:

    标签: c# asp.net web-config configurationmanager


    【解决方案1】:

    它会在首次访问属性时被缓存,因此每次请求值时它都不会从物理文件中读取。这就是为什么必须重新启动 Windows 应用程序(或 Refresh 配置)以获取最新值,以及为什么 ASP.Net 应用程序在您编辑 web.config 时自动重新启动的原因。答案 How to prevent an ASP.NET application restarting when the web.config is modified 中的参考资料讨论了为什么 ASP.Net 硬连线重启。

    我们可以使用ILSpy 验证这一点并查看 System.Configuration 的内部结构:

    public static NameValueCollection AppSettings
    {
        get
        {
            object section = ConfigurationManager.GetSection("appSettings");
            if (section == null || !(section is NameValueCollection))
            {
                throw new ConfigurationErrorsException(SR.GetString("Config_appsettings_declaration_invalid"));
            }
            return (NameValueCollection)section;
        }
    }
    

    起初,这确实看起来每次都会获取该部分。查看 GetSection:

    public static object GetSection(string sectionName)
    {
        if (string.IsNullOrEmpty(sectionName))
        {
            return null;
        }
        ConfigurationManager.PrepareConfigSystem();
        return ConfigurationManager.s_configSystem.GetSection(sectionName);
    }
    

    这里的关键线是PrepareConfigSystem()方法;这会初始化 ConfigurationManager 持有的 IInternalConfigSystem 字段的实例 - 具体类型是 ClientConfigurationSystem

    作为此负载的一部分,Configuration 类的实例被实例化。此类实际上是配置文件的对象表示,并且似乎由静态字段中的 ClientConfigurationSystem 的 ClientConfigurationHost 属性持有 - 因此它被缓存。

    您可以通过执行以下操作(在 Windows 窗体或 WPF 应用程序中)凭经验对此进行测试:

    1. 启动您的应用程序
    2. 访问 app.config 中的值
    3. 更改 app.config
    4. 检查新值是否存在
    5. 致电ConfigurationManager.RefreshSection("appSettings")
    6. 检查是否存在新值。

    事实上,如果我只是阅读关于 RefreshSection 方法的评论,我本可以为自己节省一些时间 :-)

    /// <summary>Refreshes the named section so the next time that it is retrieved it will be re-read from disk.</summary>
    /// <param name="sectionName">The configuration section name or the configuration path and section name of the section to refresh.</param>
    

    【讨论】:

      【解决方案2】:
      var file = new FileInfo(@"\\MyConfigFilePath\Web.config");
      
      DateTime first  = file.LastAccessTime;
      
      string fn = ConfigurationManager.AppSettings["FirstName"];
      Thread.Sleep(2000);
      
      DateTime second = file.LastAccessTime;
      
      string sn = ConfigurationManager.AppSettings["Surname"];
      Thread.Sleep(2000);
      
      DateTime third = file.LastAccessTime;
      

      全部显示相同的 LastAccessTime,这意味着它在启动时被缓存。

      string fn1 = ConfigurationManager.AppSettings["FirstName"];
      Thread.Sleep(2000);
      
      DateTime fourth = file.LastAccessTime;
      

      【讨论】:

        【解决方案3】:

        简单的答案是否定的,它并不总是从文件中读取它。正如一些人所建议的那样,如果文件被更改,那么 IIS 会执行重新启动,但并非总是如此!如果你想保证你从文件中读取最新的值而不是缓存,你需要这样调用:

        ConfigurationManager.RefreshSection("appSettings");
        string fromFile = ConfigurationManager.AppSettings.Get(key) ?? string.Empty;
        

        还有一个我在代码中使用的示例:

        /// ======================================================================================
        /// <summary>
        /// Refreshes the settings from disk and returns the specific setting so guarantees the
        /// value is up to date at the expense of disk I/O.
        /// </summary>
        /// <param name="key">The setting key to return.</param>
        /// <remarks>This method does involve disk I/O so should not be used in loops etc.</remarks>
        /// <returns>The setting value or an empty string if not found.</returns>
        /// ======================================================================================
        private string RefreshFromDiskAndGetSetting(string key)
        {
            // Always read from the disk to get the latest setting, this will add some overhead but
            // because this is done so infrequently it shouldn't cause any real performance issues
            ConfigurationManager.RefreshSection("appSettings");
            return GetCachedSetting(key);
        }
        
        /// ======================================================================================
        /// <summary>
        /// Retrieves the setting from cache so CANNOT guarantees the value is up to date but
        /// does not involve disk I/O so can be called frequently.
        /// </summary>
        /// <param name="key">The setting key to return.</param>
        /// <remarks>This method cannot guarantee the setting is up to date.</remarks>
        /// <returns>The setting value or an empty string if not found.</returns>
        /// ======================================================================================
        private string GetCachedSetting(string key)
        {
            return ConfigurationManager.AppSettings.Get(key) ?? string.Empty;
        }
        

        这使您可以非常轻松地选择(并在阅读代码时查看)是否每次都获取最新值,或者您是否不希望从应用程序启动时更改该值。

        【讨论】:

        • 你说的“不总是”是什么意思!这是设计使然,我知道 IIS 会重新启动应用程序并重新加载配置。
        猜你喜欢
        • 2021-10-03
        • 1970-01-01
        • 1970-01-01
        • 2013-12-15
        • 2012-09-21
        • 1970-01-01
        • 1970-01-01
        • 2012-10-13
        • 1970-01-01
        相关资源
        最近更新 更多