Liest ConfigurationManager.AppSettings [Key] jedes Mal aus der Datei web.config?

74

Ich frage mich, wie das ConfigurationManager.AppSettings[Key]funktioniert.

Liest es jedes Mal aus der physischen Datei, wenn ich einen Schlüssel benötige? Wenn ja, sollte ich alle App-Einstellungen meiner web.config in einem Cache lesen und dann daraus lesen?

Oder lädt ASP.NET oder IIS die Datei web.config beim Start der Anwendung nur einmal?

Wie überprüfe ich, ob bei jedem Lesevorgang auf die physische Datei zugegriffen wird? Wenn ich die web.config ändere, startet IIS meine Anwendung neu, sodass ich sie auf diese Weise nicht überprüfen kann.

Das Licht
quelle

Antworten:

92

Es wird beim ersten Zugriff auf eine Eigenschaft zwischengespeichert, sodass es nicht jedes Mal aus der physischen Datei gelesen wird, wenn Sie nach einem Wert fragen. Aus diesem Grunde ist es notwendig , eine Windows - Anwendung (oder neu zu starten Aktualisieren der config) den neuesten Wert zu erhalten, und warum eine ASP.Net app werden automatisch neu gestartet , wenn Sie bearbeiten web.config. Warum ASP.Net für einen Neustart fest verdrahtet ist, wird in den Referenzen in der Antwort erläutert . So verhindern Sie, dass eine ASP.NET-Anwendung neu gestartet wird, wenn die web.config geändert wird .

Wir können dies mit ILSpy überprüfen und uns die Interna von System.Configuration ansehen:

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;
    }
}

Auf den ersten Blick sieht es tatsächlich so aus, als würde es jedes Mal den Abschnitt bekommen. GetSection betrachten:

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

Die kritische Linie hier ist die PrepareConfigSystem()Methode; Dadurch wird eine Instanz des IInternalConfigSystemvom ConfigurationManager gehaltenen Felds initialisiert - der konkrete Typ istClientConfigurationSystem

Im Rahmen dieses Ladevorgangs wird eine Instanz der Konfigurationsklasse instanziiert. Diese Klasse ist effektiv eine Objektdarstellung der Konfigurationsdatei und scheint von der ClientConfigurationHost-Eigenschaft des ClientConfigurationSystems in einem statischen Feld gehalten zu werden - daher wird sie zwischengespeichert.

Sie können dies empirisch testen, indem Sie Folgendes tun (in einer Windows Form- oder WPF-App):

  1. Starten Sie Ihre App
  2. Greifen Sie auf einen Wert in app.config zu
  3. Nehmen Sie eine Änderung an app.config vor
  4. Überprüfen Sie, ob der neue Wert vorhanden ist
  5. Anruf ConfigurationManager.RefreshSection("appSettings")
  6. Überprüfen Sie, ob der neue Wert vorhanden ist.

Tatsächlich hätte ich mir etwas Zeit sparen können, wenn ich nur den Kommentar zur RefreshSection- Methode gelesen hätte :-)

/// <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>
Strich
quelle
7

Die einfache Antwort lautet Nein, sie liest sie nicht immer aus der Datei. Wie einige vorgeschlagen haben, führt IIS einen Neustart durch, wenn die Datei geändert wird, aber nicht immer! Wenn Sie sicherstellen möchten, dass Sie den neuesten Wert aus der Datei und nicht den Cache lesen, müssen Sie Folgendes aufrufen:

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

Und ein Beispiel, das ich in meinem Code verwende:

/// ======================================================================================
/// <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;
}

Auf diese Weise können Sie ganz einfach auswählen (und beim Lesen von Code sehen), ob Sie jedes Mal den neuesten Wert erhalten oder ob Sie nicht erwarten, dass sich der Wert ab dem Start der Anwendung ändert.

Belogix
quelle
1
was meinst du mit "nicht immer"! Ich habe verstanden, dass IIS die App neu startet und die Konfiguration neu lädt.
Das Licht
1
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;

Alle zeigen dieselbe LastAccessTime an, was bedeutet, dass sie beim Start zwischengespeichert wird.

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

DateTime fourth = file.LastAccessTime;
Das Licht
quelle