.NET-Konfiguration (app.config / web.config / settings.settings)

162

Ich habe eine .NET-Anwendung, die verschiedene Konfigurationsdateien für Debug- und Release-Builds enthält. Beispielsweise verweist die Datei debug app.config auf einen Entwicklungs- SQL Server den das Debuggen aktiviert ist, und das Release-Ziel zeigt auf den Live-SQL Server. Es gibt auch andere Einstellungen, von denen einige beim Debuggen / Freigeben unterschiedlich sind.

Ich verwende derzeit zwei separate Konfigurationsdateien (debug.app.config und release.app.config). Ich habe ein Build-Ereignis im Projekt, das besagt, wenn dies ein Release-Build ist, kopieren Sie release.app.config nach app.config, andernfalls kopieren Sie debug.app.config nach app.config.

Das Problem ist, dass die Anwendung ihre Einstellungen anscheinend aus der Datei settings.settings bezieht. Daher muss ich settings.settings in Visual Studio öffnen, wodurch ich aufgefordert werde, die Einstellungen zu ändern, damit ich die Änderungen akzeptiere, settings.settings speichere und habe neu erstellen, damit die richtigen Einstellungen verwendet werden.

Gibt es eine bessere / empfohlene / bevorzugte Methode, um einen ähnlichen Effekt zu erzielen? Oder habe ich das auch völlig falsch angegangen und gibt es einen besseren Ansatz?

Gavin
quelle
Ich möchte das Debuggen in Windows deaktivieren. Ich habe versucht, alle Kontrollkästchen in den Debug-Einstellungen zu deaktivieren, aber ich konnte trotzdem die Bin Release Exe debuggen. Jeder hilft mir dabei.
Vinoth Narayan

Antworten:

62

Alle Konfigurationen, die sich je nach Umgebung unterscheiden können, sollten auf Maschinenebene und nicht auf Anwendungsebene gespeichert werden . (Weitere Informationen zu Konfigurationsebenen.)

Dies sind die Arten von Konfigurationselementen, die ich normalerweise auf Maschinenebene speichere:

Wenn jede Umgebung (Entwickler, Integration, Test, Phase, Live) ihre eigenen eindeutigen Einstellungen im Verzeichnis c: \ Windows \ Microsoft.NET \ Framework64 \ v2.0.50727 \ CONFIG hat , können Sie Ihren Anwendungscode heraufstufen zwischen Umgebungen ohne Änderungen nach dem Bau.

Und natürlich wird der Inhalt des CONFIG-Verzeichnisses auf Maschinenebene in einem anderen Repository oder einer anderen Ordnerstruktur Ihrer App versionierungsgesteuert. Durch den intelligenten Einsatz von configSource können Sie Ihre .config-Dateien benutzerfreundlicher gestalten .

Ich mache das seit 7 Jahren in über 200 ASP.NET-Anwendungen bei mehr als 25 verschiedenen Unternehmen. (Ich versuche nicht zu prahlen, möchte Sie nur wissen lassen, dass ich noch nie eine Situation gesehen habe, in der dieser Ansatz nicht funktioniert.)

Portman
quelle
3
Was ist mit einer Situation, in der Sie den Webserver nicht steuern und daher die Konfiguration auf Maschinenebene nicht ändern können? Beispiele hierfür sind ein Webserver eines Drittanbieters oder ein Webserver, der von mehreren Abteilungen eines Unternehmens gemeinsam genutzt wird.
RationalGeek
1
Würde nicht funktionieren. Aber im Zeitalter der virtuellen Maschinen, Amazon EC2 und 400-Dollar-Server von Dell, tut jemand wirklich etwas Ernstes auf gemeinsam genutzten Maschinen? Ich versuche überhaupt nicht, gefühllos zu sein - ich denke wirklich, wenn Sie auf einem gemeinsam genutzten Webserver arbeiten, sollten Sie eine Neubewertung vornehmen.
Portman
7
Die meisten Unternehmen, bei denen ich mit internen Sites gearbeitet habe, hosten mehrere Anwendungen auf einem Server - dort müsste eine Neubewertung auf Unternehmensebene durchgeführt werden
MPritchard
Mehrere Anwendungen auf einem Server sind in Ordnung, solange sich alle Apps in derselben "Umgebung" befinden. Das heißt, Sie möchten nicht, dass sich die LIVE-Instanz von App1 auf demselben Server befindet wie die DEV-Instanz von App2. Beispielsweise werden Ihre SMTP-Einstellungen für alle Ihre Anwendungen freigegeben. In der Produktion verweisen Sie auf einen echten Mailserver. In der Entwicklung zeigen Sie auf eine Datei auf der Festplatte.
Portman
7
Ich weiß, dass dies funktionieren wird, aber dies widerspricht immer noch dem, was ich empfehlen würde, wenn ich versuche, die Bereitstellung zu automatisieren. Ich denke, dass Einstellungen anwendungsspezifisch sind, sie müssen zusammen mit der Anwendung versioniert werden und sich zusammen mit ihr weiterentwickeln. Wenn man sich nur auf die Maschinenkonfiguration verlässt, wird dies meiner Meinung nach schwieriger. Ich mag es, Dinge, die sich gemeinsam ändern, zusammenzuhalten und gemeinsam einzusetzen. Wenn ich eine neue Einstellung für Dev hinzufüge, benötige ich wahrscheinlich eine entsprechende für prod.
Miguel Madero
51

Dies kann einigen Personen helfen, die sich mit Settings.settings und App.config befassen: Achten Sie im Eigenschaftenbereich auf das Attribut GenerateDefaultValueInCode, während Sie einen der Werte im Raster Settings.settings in Visual Studio bearbeiten (in meinem Fall Visual Studio 2008).

Wenn Sie GenerateDefaultValueInCode auf True setzen (True ist hier die Standardeinstellung!), Wird der Standardwert in die EXE (oder DLL) kompiliert. Sie finden ihn in der Datei eingebettet, wenn Sie ihn in einem Nur-Text-Editor öffnen.

Ich habe an einer Konsolenanwendung gearbeitet und wenn ich in der EXE-Datei einen Standardwert verwendet hatte, ignorierte die Anwendung immer die Konfigurationsdatei, die sich im selben Verzeichnis befindet! Ein ziemlicher Albtraum und keine Informationen dazu im gesamten Internet.

römisch
quelle
7
Genau das ist mir am vergangenen Wochenende passiert. Ich habe viele Haare herausgezogen, um herauszufinden, warum meine App meine Datei app.config zu ignorieren schien! Es soll eine Verbindung zu einem Webdienst herstellen und die Dienst-URL befindet sich in meiner app.config. Unbekannt für mich, als ich die Webreferenz erstellte, erstellte sie auch eine Settings.Settings-Datei UND codierte den Standardwert fest in den Code. Selbst als ich die Einstellungsdatei endlich herausfand (und entfernte), blieb dieser Standardwert im Hardcode und wurde in die Exe eingebettet. SEHR FRUSTRIEREND!! Dank dieses Beitrags kann ich jetzt dieses "Feature" loswerden
Mike K
+1 Diese Antwort ist kritisch : Wenn Sie möchten, dass Ihre Einstellung in die Datei app.config aufgenommen wird, setzen Sie das Attribut GenerateDefaultValueInCode auf False (der Standardwert ist True).
Sabuncu
34

Hier gibt es eine verwandte Frage:

Verbessern Sie Ihren Build-Prozess

Konfigurationsdateien bieten eine Möglichkeit, die Einstellungen zu überschreiben:

<appSettings file="Local.config">

Anstatt zwei (oder mehr) Dateien einzuchecken, checken Sie nur die Standardkonfigurationsdatei ein und fügen dann auf jedem Zielcomputer eine Local.config mit nur dem Abschnitt appSettings ein, der die Überschreibungen für diesen bestimmten Computer enthält.

Wenn Sie Konfigurationsabschnitte verwenden, lautet das Äquivalent:

configSource="Local.config"

Natürlich ist es eine gute Idee, Sicherungskopien aller Local.config-Dateien von anderen Computern zu erstellen und sie irgendwo einzuchecken, aber nicht als Teil der eigentlichen Lösungen. Jeder Entwickler fügt der Datei Local.config ein "Ignorieren" hinzu, damit sie nicht eingecheckt wird, wodurch die Datei aller anderen überschrieben wird.

(Sie müssen es eigentlich nicht "Local.config" nennen, das ist genau das, was ich benutze)

Eric Z Bart
quelle
14

Nach dem, was ich lese, klingt es so, als würden Sie Visual Studio für Ihren Erstellungsprozess verwenden. Haben Sie darüber nachgedacht, stattdessen MSBuild und Nant zu verwenden?

Die XML-Syntax von Nant ist etwas seltsam, aber sobald Sie sie verstanden haben, wird es ziemlich trivial, das zu tun, was Sie erwähnt haben.

<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>
Steven Williams
quelle
8

Wir haben früher Web Deployment-Projekte verwendet, sind aber seitdem auf NAnt migriert. Anstatt verschiedene Einstellungsdateien zu verzweigen und zu kopieren, binden wir die Konfigurationswerte derzeit direkt in das Build-Skript ein und fügen sie über xmlpoke-Tasks in unsere Konfigurationsdateien ein:

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

In beiden Fällen können Ihre Konfigurationsdateien beliebige Entwicklerwerte haben und funktionieren problemlos in Ihrer Entwicklungsumgebung, ohne dass Ihre Produktionssysteme beschädigt werden. Wir haben festgestellt, dass Entwickler die Build-Skriptvariablen beim Testen weniger willkürlich ändern. Daher waren versehentliche Fehlkonfigurationen seltener als bei anderen Techniken, die wir ausprobiert haben, obwohl es immer noch erforderlich ist, jede Variable zu Beginn des Prozesses hinzuzufügen, damit Der dev-Wert wird standardmäßig nicht auf prod verschoben.

Jasondoucette
quelle
7

Mein aktueller Arbeitgeber hat dieses Problem gelöst, indem er zuerst die Entwicklungsstufe (Debug, Stage, Live usw.) in die Datei machine.config eingefügt hat. Dann schrieben sie Code, um das aufzunehmen und die richtige Konfigurationsdatei zu verwenden. Dadurch wurde das Problem mit der falschen Verbindungszeichenfolge nach der Bereitstellung der App behoben.

Sie haben kürzlich einen zentralen Webservice geschrieben, der die richtige Verbindungszeichenfolge aus dem Wert im Wert machine.config zurücksendet.

Ist das die beste Lösung? Wahrscheinlich nicht, aber es funktioniert für sie.

Hector Sosa Jr.
quelle
1
Eigentlich finde ich das verdammt elegant, da ich die verschiedenen Konfigurationsversionen gerne in einer Lösung sichtbar halte, auch wenn sie nicht live sind.
Annakata
1
Dies ist eine sehr interessante Lösung. Würde gerne ein Beispiel dafür in Aktion betrachten.
Mike K
5

Eine der Lösungen, die mir gut geholfen haben, war die Verwendung eines WebDeploymentProject. Ich hatte 2/3 verschiedene web.config-Dateien auf meiner Site und beim Veröffentlichen kopierte ich je nach ausgewähltem Konfigurationsmodus (Release / Staging / etc ...) über die Web.Release.config und benannte sie in Web um. Konfigurieren Sie im AfterBuild-Ereignis und löschen Sie diejenigen, die ich nicht benötige (z. B. 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>
Adam Vigh
quelle
3

Unser Projekt hat das gleiche Problem, bei dem wir Konfigurationen für dev, qa, uat und prod pflegen mussten. Folgendes haben wir befolgt (gilt nur, wenn Sie mit MSBuild vertraut sind):

Verwenden Sie MSBuild mit der Erweiterung der MSBuild Community-Aufgaben. Es enthält die Aufgabe 'XmlMassUpdate', mit der Einträge in jeder XML-Datei massenweise aktualisiert werden können, sobald Sie den richtigen Knoten für den Anfang angegeben haben.

Implementieren:

1) Sie benötigen eine Konfigurationsdatei mit Ihren dev env-Einträgen. Dies ist die Konfigurationsdatei in Ihrer Lösung.

2) Sie benötigen eine 'Substitutions.xml'-Datei, die nur die Einträge enthält, die für jede Umgebung UNTERSCHIEDLICH sind (meistens appSettings und ConnectionStrings). Einträge, die sich in der Umgebung nicht ändern, müssen nicht in diese Datei eingefügt werden. Sie können in der Datei web.config der Lösung gespeichert werden und werden von der Aufgabe nicht berührt

3) Rufen Sie in Ihrer Build-Datei einfach die XML-Massenaktualisierungsaufgabe auf und geben Sie die richtige Umgebung als Parameter an.

Siehe Beispiel unten:

    <!-- 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>

Ersetzen Sie '$ Environment' durch 'QA' oder 'Prod', je nachdem, was env. Sie bauen für. Beachten Sie, dass Sie an einer Kopie einer Konfigurationsdatei und nicht an der eigentlichen Konfigurationsdatei selbst arbeiten sollten, um mögliche nicht behebbare Fehler zu vermeiden.

Führen Sie einfach die Build-Datei aus und verschieben Sie die aktualisierte Konfigurationsdatei in Ihre Bereitstellungsumgebung. Fertig!

Lesen Sie Folgendes, um einen besseren Überblick zu erhalten:

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

Punit Vora
quelle
2

Wie Sie habe ich auch 'multi' app.config eingerichtet - zB app.configDEV, app.configTEST, app.config.LOCAL. Ich sehe einige der vorgeschlagenen hervorragenden Alternativen, aber wenn Ihnen die Funktionsweise gefällt, würde ich Folgendes hinzufügen:

Ich habe ein
<appSettings>
<add key = "Env" value = "[Local] "/> für jede App eine, die ich der Benutzeroberfläche in der Titelleiste hinzufüge: from ConfigurationManager.AppSettings.Get ("Env");

Ich benenne die Konfiguration einfach in die umbenannte um (ich habe ein Projekt mit 8 Apps mit viel Datenbank- / WCF-Konfiguration gegen 4 Abende). Um mit Clickonce in jedem zu implementieren, ändere ich 4 Einstellungen im Projekt und gehe. (das würde ich gerne automatisieren)

Ich muss nur daran denken, nach einer Änderung "alle zu bereinigen", da die alte Konfiguration nach einer manuellen Umbenennung "hängen bleibt". (Was meiner Meinung nach das Problem mit den Einstellungen beheben wird).

Ich finde das funktioniert wirklich gut (eines Tages werde ich Zeit bekommen, mir MSBuild / NAnt anzuschauen)

Tony Trembath-Drake
quelle
0

Web.config:

Web.config wird benötigt, wenn Sie Ihre Anwendung auf IIS hosten möchten. Web.config ist eine obligatorische Konfigurationsdatei für IIS, um zu konfigurieren, wie es sich als Reverse-Proxy vor Kestrel verhält. Sie müssen eine web.config pflegen, wenn Sie sie auf IIS hosten möchten.

AppSetting.json:

Für alles andere, was IIS nicht betrifft, verwenden Sie AppSetting.json. AppSetting.json wird für das Asp.Net Core-Hosting verwendet. ASP.NET Core verwendet die Umgebungsvariable "ASPNETCORE_ENVIRONMENT", um die aktuelle Umgebung zu bestimmen. Wenn Sie Ihre Anwendung ausführen, ohne diesen Wert festzulegen, wird standardmäßig automatisch die Produktionsumgebung verwendet und die Datei "AppSetting.production.json" verwendet. Wenn Sie über Visual Studio debuggen, wird die Umgebung auf "Entwicklung" gesetzt, sodass "AppSetting.json" verwendet wird. Auf dieser Website erfahren Sie, wie Sie die Hosting-Umgebungsvariable unter Windows festlegen.

App.config:

App.config ist eine weitere Konfigurationsdatei, die von .NET verwendet wird und hauptsächlich für Windows Forms, Windows Services, Konsolenanwendungen und WPF-Anwendungen verwendet wird. Wenn Sie Ihr Asp.Net Core-Hosting über die Konsolenanwendung starten, wird auch app.config verwendet.


TL; DR

Die Auswahl der Konfigurationsdatei hängt von der Hosting-Umgebung ab, die Sie für den Dienst auswählen. Wenn Sie IIS zum Hosten Ihres Dienstes verwenden, verwenden Sie eine Web.config-Datei. Wenn Sie eine andere Hosting-Umgebung verwenden, verwenden Sie eine App.config-Datei. Weitere Informationen finden Sie in der Dokumentation zum Konfigurieren von Diensten mithilfe von Konfigurationsdateien sowie unter Konfiguration in ASP.NET Core.

Alper Ebicoglu
quelle
0

Oben steht asp.net. Warum also nicht Ihre Einstellungen in der Datenbank speichern und einen benutzerdefinierten Cache verwenden, um sie abzurufen?

Der Grund, warum wir es getan haben, ist, dass es (für uns) einfacher ist, die kontinuierliche Datenbank zu aktualisieren, als die Erlaubnis zu erhalten, Produktionsdateien kontinuierlich zu aktualisieren.

Beispiel für einen benutzerdefinierten Cache:

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
    }
}
Gefangener NULL
quelle