Configurazione .NET (app.config / web.config / settings.settings)


162

Ho un'applicazione .NET che ha diversi file di configurazione per build di debug e release. Ad esempio, il file app.config di debug punta a un SQL Server di sviluppo con debug abilitato e la destinazione del rilascio punta a SQL Server live. Ci sono anche altre impostazioni, alcune delle quali sono diverse nel debug / rilascio.

Attualmente uso due file di configurazione separati (debug.app.config e release.app.config). Ho un evento di build sul progetto che dice se si tratta di una build di rilascio, quindi copia release.app.config in app.config, altrimenti copia debug.app.config in app.config.

Il problema è che l'applicazione sembra ottenere le sue impostazioni dal file settings.settings, quindi devo aprire settings.settings in Visual Studio che mi chiede che le impostazioni sono cambiate, quindi accetto le modifiche, salvo settings.settings e ho per ricostruire per farlo usare le impostazioni corrette.

Esiste un metodo migliore / raccomandato / preferito per ottenere un effetto simile? O ugualmente, ho affrontato questo completamente sbagliato e c'è un approccio migliore?


Voglio disabilitare il debug in Windows da, ho provato deselezionando tutte le caselle di controllo nelle impostazioni di debug, ma potrei ancora eseguire il debug del file exe di rilascio bin. Chiunque mi aiuti su questo ..
Vinoth Narayan,

Risposte:


62

Qualsiasi configurazione che potrebbe differire tra ambienti dovrebbe essere archiviata a livello di macchina , non a livello di applicazione . (Ulteriori informazioni sui livelli di configurazione.)

Questi sono i tipi di elementi di configurazione che in genere memorizzo a livello di macchina:

Quando ogni ambiente (sviluppatore, integrazione, test, stage, live) ha le proprie impostazioni uniche nella directory c: \ Windows \ Microsoft.NET \ Framework64 \ v2.0.50727 \ CONFIG , è possibile promuovere il codice dell'applicazione tra ambienti senza alcuna modifiche post-build.

E ovviamente, i contenuti della directory CONFIG a livello di macchina vengono controllati dalla versione in un repository diverso o in una struttura di cartelle diversa dalla tua app. Puoi rendere i tuoi file .config più compatibili con il controllo del codice sorgente attraverso l'uso intelligente di configSource .

Lo faccio da 7 anni, su oltre 200 applicazioni ASP.NET in oltre 25 aziende diverse. (Non sto cercando di vantarmi, voglio solo farti sapere che non ho mai visto una situazione in cui questo approccio non funziona.)


3
Che dire di una situazione in cui non si controlla il server Web e quindi non è possibile modificare la configurazione a livello di macchina? Gli esempi includono un server Web di terze parti o un server Web condiviso tra più reparti di un'azienda.
RationalGeek,

1
Non funzionerebbe Ma nell'era delle macchine virtuali, Amazon EC2 e $ 400 server di Dell, qualcuno fa davvero qualcosa di serio su macchine condivise? Non cercare di essere insensibile - penso davvero che se stai lavorando su un server web condiviso dovresti rivalutare.
Portman,

7
La maggior parte delle aziende in cui ho lavorato con siti interni ospitano più applicazioni su un server - lì una rivalutazione dovrebbe essere fatta a livello aziendale
MPritchard,

Più applicazioni su un server vanno bene purché le app siano tutte nello stesso "ambiente". Vale a dire, non vorresti l'istanza LIVE di App1 sullo stesso server dell'istanza DEV di App2. Ad esempio, le impostazioni SMTP verranno condivise tra tutte le applicazioni. In produzione, si punta a un vero server di posta; in fase di sviluppo, si punta a un file su disco.
Portman,

7
So che funzionerà, ma questo va ancora contro ciò che consiglierei quando provo ad automatizzare la distribuzione. Penso che le impostazioni siano specifiche dell'applicazione, devono essere controllate con la versione insieme all'applicazione ed evolversi insieme ad essa. Affidarsi alla configurazione della macchina cambia solo, secondo me rende più difficile. Mi piace tenere insieme le cose che cambiano insieme e distribuirle insieme. Se aggiungo una nuova impostazione per Dev probabilmente ho bisogno di una equivalente per prod.
Miguel Madero,

52

Questo potrebbe aiutare alcune persone che si occupano di Settings.settings e App.config: attenzione all'attributo GenerateDefaultValueInCode nel riquadro Proprietà mentre si modificano i valori nella griglia Settings.settings in Visual Studio (Visual Studio 2008 nel mio caso).

Se si imposta GenerateDefaultValueInCode su True (True è l'impostazione predefinita qui!), Il valore predefinito viene compilato in EXE (o DLL), è possibile trovarlo incorporato nel file quando lo si apre in un editor di testo semplice.

Stavo lavorando su un'applicazione console e se avevo impostato il file EXE in modo predefinito, l'applicazione ignorava sempre il file di configurazione inserito nella stessa directory! Piuttosto un incubo e nessuna informazione al riguardo su Internet.


7
Questo è esattamente quello che mi è successo lo scorso fine settimana. Ho tirato fuori molti capelli cercando di capire perché la mia app sembrava ignorare il mio file app.config! Dovrebbe connettersi a un servizio Web e l'URL del servizio è nella mia app.config. A mia insaputa, quando ho creato il riferimento Web, ha anche creato un file Settings.Settings E ha codificato il valore predefinito nel codice. Anche quando ho finalmente capito (e rimosso) il file delle impostazioni, quel valore predefinito è rimasto nel hardcode e mi è stato incorporato nell'esempio. MOLTO FRUSTRANTE!! Grazie a questo post, ora posso liberarmi di quella "caratteristica"
Mike K

+1 Questa è la risposta fondamentale : se vuoi che le tue impostazioni vadano nel file app.config, imposta il suo attributo GenerateDefaultValueInCode su False (il valore predefinito è True).
Sabuncu,

34

C'è una domanda correlata qui:

Migliorare il processo di compilazione

I file di configurazione vengono forniti con un modo per sovrascrivere le impostazioni:

<appSettings file="Local.config">

Invece di archiviare due (o più) file, si controlla solo il file di configurazione predefinito, quindi su ogni macchina di destinazione si inserisce Local.config, con solo la sezione appSettings che ha le sostituzioni per quella macchina specifica.

Se si utilizzano sezioni di configurazione, l'equivalente è:

configSource="Local.config"

Naturalmente, è una buona idea fare copie di backup di tutti i file Local.config da altre macchine e controllarli da qualche parte, ma non come parte delle soluzioni reali. Ogni sviluppatore inserisce un "ignore" nel file Local.config in modo che non venga archiviato, il che sovrascriverebbe il file di tutti gli altri.

(In realtà non devi chiamarlo "Local.config", è proprio quello che uso)


14

Da quello che sto leggendo, sembra che tu stia usando Visual Studio per il tuo processo di compilazione. Hai mai pensato di usare MSBuild e Nant ?

La sintassi xml di Nant è un po 'strana ma una volta capito, fare ciò che hai citato diventa piuttosto banale.

<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

Usavamo progetti di distribuzione Web, ma da allora siamo passati a NAnt. Invece di ramificare e copiare diversi file di impostazioni, attualmente incorporiamo i valori di configurazione direttamente nello script di build e li iniettiamo nei nostri file di configurazione tramite attività xmlpoke:

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

In entrambi i casi, i tuoi file di configurazione possono avere qualunque valore di sviluppatore desideri e funzioneranno bene all'interno del tuo ambiente di sviluppo senza rompere i sistemi di produzione. Abbiamo scoperto che è meno probabile che gli sviluppatori modifichino arbitrariamente le variabili dello script di build durante il test delle cose, quindi configurazioni errate accidentali sono state più rare rispetto ad altre tecniche che abbiamo provato, anche se è ancora necessario aggiungere ogni var all'inizio del processo in modo che il valore dev non viene spinto a prod per impostazione predefinita.


7

Il mio attuale datore di lavoro ha risolto questo problema inserendo innanzitutto il livello di sviluppo (debug, stage, live, ecc.) Nel file machine.config. Quindi hanno scritto il codice per raccoglierlo e usare il giusto file di configurazione. Ciò ha risolto il problema con la stringa di connessione errata dopo la distribuzione dell'app.

Di recente hanno scritto un servizio web centrale che restituisce la stringa di connessione corretta dal valore nel valore machine.config.

Questa è la soluzione migliore? Probabilmente no, ma funziona per loro.


1
In realtà penso che sia dannatamente elegante, dal momento che mi piace mantenere le varie versioni di configurazione all'interno di una soluzione, anche se non sono attive.
annakata,

1
Questa è una soluzione molto intrigante. Mi piacerebbe guardare un esempio di questo in azione.
Mike K

5

Una delle soluzioni che mi ha funzionato bene è stata utilizzare un WebDeploymentProject. Avevo 2/3 diversi file web.config nel mio sito e in fase di pubblicazione, a seconda della modalità di configurazione selezionata (release / staging / ecc ...), avrei copiato sul Web.Release.config e rinominato in Web. config nell'evento AfterBuild ed elimina quelli di cui non ho bisogno (ad esempio 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

Il nostro progetto ha lo stesso problema in cui abbiamo dovuto mantenere le configurazioni per dev, qa, uat e prod. Ecco cosa abbiamo seguito (vale solo se hai familiarità con MSBuild):

Utilizzare MSBuild con l'estensione delle attività della community MSBuild. Include l'attività 'XmlMassUpdate' che può 'aggiornare in massa' le voci in qualsiasi file XML dopo avergli assegnato il nodo corretto.

Implementare:

1) Devi avere un file di configurazione che avrà le tue voci di sviluppo; questo è il file di configurazione nella tua soluzione.

2) È necessario disporre di un file 'Substitutions.xml', che contenga solo le voci DIVERSE (appSettings e ConnectionStrings principalmente) per ciascun ambiente. Le voci che non cambiano nell'ambiente non devono essere inserite in questo file. Possono vivere nel file web.config della soluzione e non verranno toccati dall'attività

3) Nel tuo file di build, chiama semplicemente l'attività di aggiornamento di massa XML e fornisci l'ambiente giusto come parametro.

Vedi l'esempio seguente:

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

sostituire "$ Environment" con "QA" o "Prod" in base a quale env. stai costruendo per. Si noti che è necessario lavorare su una copia di un file di configurazione e non sul file di configurazione stesso per evitare eventuali errori non recuperabili.

Basta eseguire il file di build e quindi spostare il file di configurazione aggiornato nel proprio ambiente di distribuzione e il gioco è fatto!

Per una migliore visione d'insieme, leggi questo:

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


2

Come te ho anche impostato app.config 'multi' - ad es. App.configDEV, app.configTEST, app.config.LOCAL. Vedo alcune delle eccellenti alternative suggerite, ma se ti piace il modo in cui funziona per te, aggiungerei quanto segue:

Ho una
<appSettings>
<add key = "Env" value = "[Local] "/> app per ogni app che aggiungo all'interfaccia utente nella barra del titolo: da ConfigurationManager.AppSettings.Get ("Env");

Ho appena rinominato la configurazione con quella a cui sto puntando (ho un progetto con 8 app con un sacco di database / configurazione del wc contro 4 evenioments). Per distribuire con clickonce in ciascuno cambio 4 sezioni nel progetto e via. (questo mi piacerebbe automatizzare)

Il mio unico problema è ricordare di "pulire tutto" dopo una modifica, poiché la vecchia configurazione è "bloccata" dopo una ridenominazione manuale. (Che penso che risolverà il problema di impostazione.)

Trovo che funzioni davvero bene (un giorno avrò il tempo di guardare MSBuild / NAnt)


0

web.config:

Web.config è necessario quando si desidera ospitare l'applicazione su IIS. Web.config è un file di configurazione obbligatorio per IIS per configurare come si comporterà come proxy inverso di fronte a Kestrel. È necessario mantenere un web.config se si desidera ospitarlo su IIS.

AppSetting.json:

Per tutto il resto che non riguarda IIS, si utilizza AppSetting.json. AppSetting.json viene utilizzato per l'hosting Asp.Net Core. ASP.NET Core utilizza la variabile di ambiente "ASPNETCORE_ENVIRONMENT" per determinare l'ambiente corrente. Per impostazione predefinita, se si esegue l'applicazione senza impostare questo valore, verrà automaticamente impostato sull'ambiente di produzione e utilizzerà il file "AppSetting.production.json". Quando esegui il debug tramite Visual Studio, l'ambiente viene impostato su Sviluppo, quindi utilizza "AppSetting.json". Vedere questo sito Web per capire come impostare la variabile di ambiente di hosting su Windows.

App.config:

App.config è un altro file di configurazione utilizzato da .NET che viene utilizzato principalmente per Windows Form, Servizi Windows, App console e applicazioni WPF. Quando si avvia l'hosting Asp.Net Core tramite l'applicazione console viene anche utilizzata app.config.


TL; DR

La scelta del file di configurazione è determinata dall'ambiente di hosting scelto per il servizio. Se si utilizza IIS per ospitare il proprio servizio, utilizzare un file Web.config. Se si utilizza qualsiasi altro ambiente di hosting, utilizzare un file App.config. Vedere la sezione Configurazione dei servizi mediante la documentazione dei file di configurazione e consultare anche Configurazione in ASP.NET Core.


0

Dice asp.net sopra, quindi perché non salvare le impostazioni nel database e utilizzare una cache personalizzata per recuperarle?

Il motivo per cui l'abbiamo fatto perché è più facile (per noi) aggiornare continuamente il database piuttosto che ottenere l'autorizzazione ad aggiornare continuamente i file di produzione.

Esempio di una cache personalizzata:

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
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.