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

162

Mam aplikację .NET, która ma różne pliki konfiguracyjne dla kompilacji debugowania i wydania. Na przykład plik debugowania app.config wskazuje na programistyczny SQL Server, który ma włączone debugowanie, a miejsce docelowe wydania wskazuje na działający SQL Server. Istnieją również inne ustawienia, z których niektóre różnią się w debugowaniu / wydaniu.

Obecnie używam dwóch oddzielnych plików konfiguracyjnych (debug.app.config i release.app.config). Mam zdarzenie kompilacji w projekcie, które mówi, że jeśli jest to kompilacja wydania, skopiuj release.app.config do app.config, w przeciwnym razie skopiuj debug.app.config do app.config.

Problem polega na tym, że aplikacja wydaje się pobierać ustawienia z pliku settings.settings, więc muszę otworzyć settings.settings w programie Visual Studio, który następnie wyświetla monit, że ustawienia się zmieniły, więc akceptuję zmiany, zapisuję ustawienia. Ustawienia i mam odbudować, aby używał poprawnych ustawień.

Czy jest lepsza / zalecana / preferowana metoda osiągnięcia podobnego efektu? Albo też, czy podejdę do tego całkowicie źle i czy jest lepsze podejście?

Gavin
źródło
Chcę wyłączyć debugowanie w systemie Windows z, próbowałem przez odznaczenie wszystkich pól wyboru w ustawieniach debugowania, ale nadal mogłem zdebugować bin release exe .. Każdy mi w tym pomoże ..
Vinoth Narayan

Odpowiedzi:

62

Każda konfiguracja, która może się różnić w różnych środowiskach, powinna być przechowywana na poziomie komputera , a nie aplikacji . (Więcej informacji na temat poziomów konfiguracji).

Oto rodzaje elementów konfiguracji, które zwykle przechowuję na poziomie komputera:

Gdy każde środowisko (programista, integracja, test, etap, wersja na żywo) ma własne unikalne ustawienia w katalogu c: \ Windows \ Microsoft.NET \ Framework64 \ v2.0.50727 \ CONFIG , możesz promować kod aplikacji między środowiskami bez modyfikacje po kompilacji.

I oczywiście zawartość katalogu CONFIG na poziomie komputera jest kontrolowana przez wersję w innym repozytorium lub w innej strukturze folderów z Twojej aplikacji. Możesz uczynić swoje pliki .config bardziej przyjaznymi dla kontroli źródła poprzez inteligentne użycie configSource .

Robię to od 7 lat na ponad 200 aplikacjach ASP.NET w ponad 25 różnych firmach. (Nie próbuję się chwalić, chcę tylko powiedzieć, że nigdy nie widziałem sytuacji, w której takie podejście nie działa.)

Portman
źródło
3
A co z sytuacją, w której nie kontrolujesz serwera WWW i dlatego nie możesz zmienić konfiguracji na poziomie komputera? Przykłady obejmują serwer WWW innej firmy lub serwer WWW współużytkowany przez wiele działów w przedsiębiorstwie.
RationalGeek
1
Nie zadziała. Ale czy w dobie maszyn wirtualnych, Amazon EC2 i serwerów Dell za 400 USD, czy ktoś naprawdę robi coś poważnego na współdzielonych maszynach? W ogóle nie próbuję być bezduszny - naprawdę uważam, że jeśli pracujesz na współdzielonym serwerze internetowym, powinieneś ponownie ocenić.
Portman,
7
Większość firm, w których pracowałem, z witrynami wewnętrznymi obsługuje wiele aplikacji na jednym serwerze - tam
ponowna ocena
Wiele aplikacji na jednym serwerze jest w porządku, o ile wszystkie aplikacje znajdują się w tym samym „środowisku”. To znaczy, nie chciałbyś, aby instancja LIVE App1 znajdowała się na tym samym serwerze, co instancja DEV aplikacji App2. Na przykład ustawienia SMTP byłyby wspólne dla wszystkich aplikacji. W środowisku produkcyjnym wskazujesz na prawdziwy serwer pocztowy; w trakcie opracowywania wskaż plik na dysku.
Portman
7
Wiem, że to zadziała, ale nadal jest to sprzeczne z tym, co zalecałbym, próbując zautomatyzować wdrażanie. Myślę, że ustawienia są specyficzne dla aplikacji, muszą być kontrolowane wersjami wraz z aplikacją i ewoluować wraz z nią. Poleganie na konfiguracji maszyny po prostu się zmienia, co moim zdaniem utrudnia. Lubię trzymać razem rzeczy, które się zmieniają i wdrażać je razem. Jeśli dodam nowe ustawienie dla Dev, prawdopodobnie potrzebuję odpowiednika dla wersji produkcyjnej.
Miguel Madero
52

Może to pomóc niektórym osobom zajmującym się Settings.settings i App.config: Uważaj na atrybut GenerateDefaultValueInCode w okienku Właściwości podczas edytowania dowolnej wartości w siatce Settings.settings w programie Visual Studio (Visual Studio 2008 w moim przypadku).

Jeśli ustawisz GenerateDefaultValueInCode na True (wartość domyślna to True!), Wartość domyślna jest kompilowana do pliku EXE (lub DLL), można ją znaleźć osadzoną w pliku po otwarciu go w edytorze zwykłego tekstu.

Pracowałem na aplikacji konsolowej i gdybym domyślnie ustawił się w pliku EXE, aplikacja zawsze ignorowała plik konfiguracyjny umieszczony w tym samym katalogu! Dość koszmar i brak informacji o tym w całym Internecie.

rzymski
źródło
7
Dokładnie to przytrafiło mi się w miniony weekend. Wyciągnąłem dużo włosów, próbując zrozumieć, dlaczego moja aplikacja ignoruje mój plik app.config! Powinien łączyć się z usługą internetową, a adres URL usługi znajduje się w moim pliku app.config. Bez mojej wiedzy, kiedy tworzyłem odwołanie do sieci, utworzył również plik Settings.Settings ORAZ zakodował wartość domyślną w kodzie. Nawet kiedy w końcu odkryłem (i usunąłem) plik ustawień, ta domyślna wartość pozostała w kodzie twardym i została osadzona w exe. BARDZO FRUSTRUJĄCE!! Dzięki temu wpisowi mogę się teraz pozbyć tej „funkcji”
Mike K
+1 Ta odpowiedź jest krytyczna : jeśli chcesz, aby Twoje ustawienie znalazło się w pliku app.config, ustaw jego atrybut GenerateDefaultValueInCode na False (wartość domyślna to True).
Sabuncu
34

Jest tu powiązane pytanie:

Poprawa procesu tworzenia

Pliki konfiguracyjne zawierają sposób na zastąpienie ustawień:

<appSettings file="Local.config">

Zamiast wpisywać dwa pliki (lub więcej), wpisujesz tylko domyślny plik konfiguracyjny, a następnie na każdym komputerze docelowym umieszczasz plik Local.config, zawierający tylko sekcję appSettings, która ma nadpisania dla tego konkretnego komputera.

Jeśli używasz sekcji konfiguracyjnych, odpowiednikiem jest:

configSource="Local.config"

Oczywiście dobrze jest wykonać kopie zapasowe wszystkich plików Local.config z innych komputerów i gdzieś je sprawdzić, ale nie jako część rzeczywistych rozwiązań. Każdy programista umieszcza opcję „ignoruj” w pliku Local.config, aby nie został on wpisany, co spowodowałoby nadpisanie pliku innych osób.

(W rzeczywistości nie musisz nazywać tego „Local.config”, właśnie tego używam)

Eric Z Beard
źródło
14

Z tego, co czytam, wygląda na to, że używasz programu Visual Studio do procesu kompilacji. Czy myślałeś o używaniu zamiast tego programów MSBuild i Nant ?

Składnia XML Nanta jest trochę dziwna, ale kiedy ją zrozumiesz, robienie tego, o czym wspomniałeś, staje się dość trywialne.

<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
źródło
8

Kiedyś korzystaliśmy z projektów Web Deployment, ale od tego czasu przeprowadziliśmy migrację do NAnt. Zamiast rozgałęziania i kopiowania różnych plików ustawień, obecnie osadzamy wartości konfiguracyjne bezpośrednio w skrypcie kompilacji i wstrzykujemy je do naszych plików konfiguracyjnych za pomocą zadań xmlpoke:

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

W obu przypadkach pliki konfiguracyjne mogą mieć dowolne wartości programistyczne i będą działać dobrze w środowisku deweloperskim bez uszkadzania systemów produkcyjnych. Odkryliśmy, że programiści są mniej skłonni do arbitralnej zmiany zmiennych skryptu kompilacji podczas testowania, więc przypadkowe błędne konfiguracje były rzadsze niż w przypadku innych wypróbowanych przez nas technik, chociaż nadal konieczne jest dodawanie każdej zmiennej na początku procesu, aby wartość dev nie jest domyślnie wypychana do produktu.

jasondoucette
źródło
7

Mój obecny pracodawca rozwiązał ten problem, umieszczając najpierw poziom deweloperski (debugowanie, etap, na żywo itp.) W pliku machine.config. Następnie napisali kod, aby to odebrać i użyć odpowiedniego pliku konfiguracyjnego. To rozwiązało problem z nieprawidłowymi parametrami połączenia po wdrożeniu aplikacji.

Niedawno napisali centralną usługę sieciową, która odsyła poprawne parametry połączenia z wartości w wartości machine.config.

Czy to najlepsze rozwiązanie? Pewnie nie, ale na nich to działa.

Hector Sosa Jr
źródło
1
Właściwie myślę, że to cholernie eleganckie, ponieważ lubię utrzymywać różne wersje konfiguracji widoczne w rozwiązaniu, nawet jeśli nie są na żywo.
annakata
1
To bardzo intrygujące rozwiązanie. Chciałbym spojrzeć na przykład tego w działaniu.
Mike K
5

Jednym z rozwiązań, które działało dobrze, było użycie WebDeploymentProject. Miałem 2/3 różnych plików web.config w mojej witrynie i po opublikowaniu, w zależności od wybranego trybu konfiguracji (release / staging / etc ...), kopiowałem przez Web.Release.config i zmieniałem nazwę na web. config w zdarzeniu AfterBuild i usuń te, których nie potrzebuję (na przykład 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
źródło
3

Nasz projekt ma ten sam problem, w którym musieliśmy utrzymywać konfiguracje dla dev, qa, uat i prod. Oto, czego przestrzegaliśmy (dotyczy tylko osób zaznajomionych z programem MSBuild):

Użyj programu MSBuild z rozszerzeniem zadań społeczności MSBuild. Zawiera zadanie „XmlMassUpdate”, które może „masowo aktualizować” wpisy w dowolnym pliku XML, gdy podasz mu właściwy węzeł na początek.

Wdrożenie:

1) Musisz mieć jeden plik konfiguracyjny, który będzie zawierał wpisy dev env; to jest plik konfiguracyjny w Twoim rozwiązaniu.

2) Musisz mieć plik „Substitutions.xml”, który zawiera tylko RÓŻNE wpisy (głównie appSettings i ConnectionStrings) dla każdego środowiska. Wpisy, które nie zmieniają się w środowisku, nie muszą być umieszczane w tym pliku. Mogą mieszkać w pliku web.config rozwiązania i nie zostaną dotknięci przez zadanie

3) W pliku kompilacji po prostu wywołaj zadanie masowej aktualizacji XML i podaj odpowiednie środowisko jako parametr.

Zobacz przykład poniżej:

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

zamień „$ Environment” na „QA” lub „Prod” w zależności od tego, jakie środowisko. dla którego budujesz. Zauważ, że powinieneś pracować na kopii pliku konfiguracyjnego, a nie na samym pliku konfiguracyjnym, aby uniknąć ewentualnych niemożliwych do odzyskania błędów.

Po prostu uruchom plik kompilacji, a następnie przenieś zaktualizowany plik konfiguracyjny do środowiska wdrażania i gotowe!

Aby uzyskać lepszy przegląd, przeczytaj to:

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

Punit Vora
źródło
2

Podobnie jak ty, skonfigurowałem również „multi” app.config - np. App.configDEV, app.configTEST, app.config.LOCAL. Widzę kilka doskonałych sugerowanych alternatyw, ale jeśli podoba Ci się sposób, w jaki to działa, dodałbym:

Mam
<appSettings>
<add key = "Env" value = "[Local] "/> dla każdej aplikacji dodaję to do interfejsu użytkownika na pasku tytułowym: z ConfigurationManager.AppSettings.Get ("Env");

Po prostu zmieniam nazwę konfiguracji na tę, do której celuję (mam projekt z 8 aplikacjami z dużą ilością konfiguracji bazy danych / wcf na 4 evenioments). Aby wdrożyć za pomocą Clickonce w każdym, zmieniam 4 ustawienia w projekcie i zaczynam. (chciałbym to zautomatyzować)

Jedyne, co mi pozostaje, to pamiętać o „wyczyszczeniu wszystkiego” po zmianie, ponieważ stara konfiguracja „utknęła” po ręcznej zmianie nazwy. (Co myślę, że naprawi problem z ustawieniami).

Uważam, że działa to naprawdę dobrze (pewnego dnia znajdę czas, aby spojrzeć na MSBuild / NAnt)

Tony Trembath-Drake
źródło
0

Web.config:

Plik Web.config jest potrzebny, gdy chcesz hostować aplikację w usługach IIS. Web.config to obowiązkowy plik konfiguracyjny dla usług IIS, który umożliwia skonfigurowanie sposobu, w jaki będzie zachowywał się jako odwrotny serwer proxy przed Kestrel. Musisz utrzymywać plik web.config, jeśli chcesz go hostować w usługach IIS.

AppSetting.json:

Do wszystkiego innego, co nie dotyczy usług IIS, należy użyć AppSetting.json. AppSetting.json jest używany do hostingu Asp.Net Core. ASP.NET Core używa zmiennej środowiskowej „ASPNETCORE_ENVIRONMENT” do określenia bieżącego środowiska. Domyślnie, jeśli uruchomisz aplikację bez ustawiania tej wartości, automatycznie przejdzie ona do środowiska produkcyjnego i użyje pliku „AppSetting.production.json”. Podczas debugowania za pomocą programu Visual Studio ustawia środowisko na programowanie, więc używa „AppSetting.json”. Odwiedź tę witrynę internetową, aby dowiedzieć się, jak ustawić zmienną środowiskową hostingu w systemie Windows.

App.config:

App.config to kolejny plik konfiguracyjny używany przez .NET, który jest używany głównie w Windows Forms, Windows Services, Console Apps i WPF. Podczas uruchamiania hostingu Asp.Net Core za pośrednictwem aplikacji konsoli jest również używany plik app.config.


TL; DR

Wybór pliku konfiguracyjnego zależy od środowiska hostingu wybranego dla usługi. Jeśli używasz usług IIS do hostowania usługi, użyj pliku Web.config. Jeśli korzystasz z innego środowiska hostingu, użyj pliku App.config. Zobacz Konfigurowanie usług przy użyciu dokumentacji plików konfiguracyjnych, a także wyewidencjonuj konfigurację w ASP.NET Core.

Alper Ebicoglu
źródło
0

Mówi o asp.net powyżej, więc dlaczego nie zapisać ustawień w bazie danych i użyć niestandardowej pamięci podręcznej do ich pobrania?

Powód, dla którego to zrobiliśmy, ponieważ łatwiej nam (nam) aktualizować ciągłą bazę danych niż uzyskać pozwolenie na ciągłą aktualizację plików produkcyjnych.

Przykład niestandardowej pamięci podręcznej:

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
    }
}
Więzień ZERO
źródło