Zmienne w app.config / web.config

92

Czy w plikach app.configlub można zrobić coś podobnego do następującego web.config?

<appSettings>
 <add key="MyBaseDir" value="C:\MyBase" />
 <add key="Dir1" value="[MyBaseDir]\Dir1"/>
 <add key="Dir2" value="[MyBaseDir]\Dir2"/>
</appSettings>

Następnie chcę uzyskać dostęp do Dir2 w moim kodzie, mówiąc po prostu:

 ConfigurationManager.AppSettings["Dir2"]

Pomoże mi to, gdy zainstaluję moją aplikację na różnych serwerach i lokalizacjach, w których będę musiał zmienić tylko JEDEN wpis w całym moim app.config. (Wiem, że mogę zarządzać całą konkatenacją w kodzie, ale wolę to w ten sposób).

DeeStackOverflow
źródło
Myślę, że mówi o definiowaniu zmiennych do użycia w kluczach appSettings bezpośrednio w plikach konfiguracyjnych.
Michaël Carpentier
1
Sprawdziłem również przy użyciu deklaracji XML <! ENTITY>, ale nie jest ona obsługiwana ze względu na sposób, w jaki MS obsługuje pliki web.config.
chilltemp
Dziękuję za Twój wysiłek. Wolę nie modyfikować żadnego kodu. Kod zawiera już instrukcję mówiącą: string dir2 = ConfigurationManager.AppSettings ["Dir2"]. Chcę tylko wyczyścić plik app.config, który teraz mówi value = "D: \ blahdir \ Dir2" zamiast value = "[MyBaseDir] \ Dir2"
DeeStackOverflow

Odpowiedzi:

7

Dobre pytanie.

Myślę, że nie ma. Sądzę, że byłoby to całkiem dobrze znane, gdyby istniał łatwy sposób i widzę, że Microsoft tworzy w Visual Studio 2010 mechanizm do wdrażania różnych plików konfiguracyjnych do wdrożenia i testowania.

Powiedziawszy to jednak; Odkryłem, że w tej ConnectionStringssekcji znajduje się rodzaj symbolu zastępczego o nazwie „| DataDirectory |”. Może mógłbyś rzucić okiem, co tam pracuje ...

Oto fragment z machine.configpokazania tego:

 <connectionStrings>
    <add
        name="LocalSqlServer"
        connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
        providerName="System.Data.SqlClient"
    />
 </connectionStrings>
Arjan Einbu
źródło
To interesująca informacja. Może zmienne są dostępne za pomocą symbolu potoku („|”)? Hmm ... Ciekawe, czy to zadziała: <add key = "Dir2" value = "| MyBaseDir | \ Dir2" />
DeeStackOverflow
4
Wartość DataDirectory jest w rzeczywistości elementem danych w AppDomain. Wartość można zastąpić, używając AppDomain.CurrentDomain.SetData ("DataDirectory", dataPath); Nie testowałem, czy możesz zdefiniować inne zmienne, takie jak ta i uzyskać je „automatycznie rozszerzane” ...
Peter Lillevold
22

Nieco bardziej skomplikowaną, ale znacznie bardziej elastyczną alternatywą jest utworzenie klasy reprezentującej sekcję konfiguracji. W Twoimapp.config / web.configpliku możesz mieć to:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- This section must be the first section within the <configuration> node -->
    <configSections>
        <section name="DirectoryInfo" type="MyProjectNamespace.DirectoryInfoConfigSection, MyProjectAssemblyName" />
    </configSections>

    <DirectoryInfo>
        <Directory MyBaseDir="C:\MyBase" Dir1="Dir1" Dir2="Dir2" />
    </DirectoryInfo>
</configuration>

Następnie w swoim kodzie .NET (w moim przykładzie użyję C #) możesz utworzyć dwie klasy, takie jak ta:

using System;
using System.Configuration;

namespace MyProjectNamespace {

    public class DirectoryInfoConfigSection : ConfigurationSection {

        [ConfigurationProperty("Directory")]
        public DirectoryConfigElement Directory {
            get {
                return (DirectoryConfigElement)base["Directory"];
            }
    }

    public class DirectoryConfigElement : ConfigurationElement {

        [ConfigurationProperty("MyBaseDir")]
        public String BaseDirectory {
            get {
                return (String)base["MyBaseDir"];
            }
        }

        [ConfigurationProperty("Dir1")]
        public String Directory1 {
            get {
                return (String)base["Dir1"];
            }
        }

        [ConfigurationProperty("Dir2")]
        public String Directory2 {
            get {
                return (String)base["Dir2"];
            }
        }
        // You can make custom properties to combine your directory names.
        public String Directory1Resolved {
            get {
                return System.IO.Path.Combine(BaseDirectory, Directory1);
            }
        }
    }
}

Wreszcie, w kodzie programu możesz uzyskać dostęp do swoich app.configzmiennych, używając nowych klas, w następujący sposób:

DirectoryInfoConfigSection config =
  (DirectoryInfoConfigSection)ConfigurationManager.GetSection("DirectoryInfo");
String dir1Path = config.Directory.Directory1Resolved;  // This value will equal "C:\MyBase\Dir1"
Matt Hamsmith
źródło
1
Dzięki, ale próbuję to zrobić bez modyfikowania kodu, ponieważ jest to uciążliwe na tym etapie.
DeeStackOverflow
W ostatnim wierszu kodu występuje mały błąd (nie licząc nawiasów klamrowych): "return System.IO.Path.Combine (MyBaseDir, Dir1);" zamiast tego powinno mieć wartość „return System.IO.Path.Combine (BaseDirectory, Dir1);” lub w innym przypadku należy zmienić nazwę metody z „Base Directory” na „MyBaseDir”
TheWho
16

Możesz to zrobić, korzystając z mojej biblioteki Expansive . Dostępne również na nuget tutaj .

Został zaprojektowany z tym jako podstawowy przypadek użycia.

Umiarkowany przykład (używanie AppSettings jako domyślnego źródła do rozwijania tokenu)

W app.config:

<configuration>
    <appSettings>
        <add key="Domain" value="mycompany.com"/>
        <add key="ServerName" value="db01.{Domain}"/>
    </appSettings>
    <connectionStrings>
        <add name="Default" connectionString="server={ServerName};uid=uid;pwd=pwd;Initial Catalog=master;" provider="System.Data.SqlClient" />
    </connectionStrings>
</configuration>

Użyj metody rozszerzenia .Expand () w ciągu do rozwinięcia:

var connectionString = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
connectionString.Expand() // returns "server=db01.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"

lub

Użyj opakowania dynamicznego menedżera konfiguracji „Config” w następujący sposób (jawne wywołanie Expand () nie jest konieczne):

var serverName = Config.AppSettings.ServerName;
// returns "db01.mycompany.com"

var connectionString = Config.ConnectionStrings.Default;
// returns "server=db01.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"

Przykład zaawansowany 1 (używanie AppSettings jako domyślnego źródła do rozwijania tokenu)

W app.config:

<configuration>
    <appSettings>
        <add key="Environment" value="dev"/>
        <add key="Domain" value="mycompany.com"/>
        <add key="UserId" value="uid"/>
        <add key="Password" value="pwd"/>
        <add key="ServerName" value="db01-{Environment}.{Domain}"/>
        <add key="ReportPath" value="\\{ServerName}\SomeFileShare"/>
    </appSettings>
    <connectionStrings>
        <add name="Default" connectionString="server={ServerName};uid={UserId};pwd={Password};Initial Catalog=master;" provider="System.Data.SqlClient" />
    </connectionStrings>
</configuration>

Użyj metody rozszerzenia .Expand () w ciągu do rozwinięcia:

var connectionString = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
connectionString.Expand() // returns "server=db01-dev.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"
anderly
źródło
4
Myślę, że ta odpowiedź jest bardzo zaniżona !!
Ahmad,
Dzięki Ahmadzie! Daj mi znać, jak lubisz Expansive.
anderly
Chociaż jest to `` rozwiązanie '' ustawień aplikacji w czasie wykonywania, rozwiązuje to moje problemy z powtarzającymi się parami klucz-wartość. Dzięki temu znacznie ograniczyliśmy konserwację konfiguracji. Absolutną utopią byłoby to, że jest to wtyczka czasu kompilacji do pracy w połączeniu ze SlowCheetah. Gdybym mógł, dałbym +1 ponownie. Świetna rzecz, anderly.
Ahmad
Czy możesz podać krótki przykład, w jaki sposób można wykorzystać swoją bibliotekę, aby to osiągnąć?
Ryan Gates,
Dla każdego, kto właśnie się z tym
spotkał
4

Myślałem, że właśnie zobaczyłem to pytanie.

Krótko mówiąc, nie, w konfiguracji aplikacji nie ma interpolacji zmiennych.

Masz dwie możliwości

  1. Możesz utworzyć własne, aby zastąpić zmienne w czasie wykonywania
  2. W czasie kompilacji dostosuj konfigurację aplikacji do konkretnych specyfiki docelowego środowiska wdrażania. Kilka szczegółów na ten temat w radzeniu sobie z koszmarem konfiguracji
Scott Weinstein
źródło
To jest poprawny post. Mój poprzedni post (to samo pytanie) nie pokazywał przykładowego wpisu app.config xml. Sprawdziłem Twój link - to za dużo pracy i wolę nie spędzać tam czasu. Mamy oddzielne pliki app.config dla różnych pudełek i chcę od tego uciec.
DeeStackOverflow
3

Masz kilka opcji. Możesz to zrobić za pomocą kroku kompilacji / wdrożenia, który przetworzyłby plik konfiguracyjny, zastępując zmienne poprawną wartością.

Inną opcją byłoby zdefiniowanie własnej sekcji konfiguracji, która to obsługuje. Na przykład wyobraź sobie ten xml:

<variableAppSettings>
 <variables>
    <add key="@BaseDir" value="c:\Programs\Widget"/>
 </variables>
 <appSettings>
    <add key="PathToDir" value="@BaseDir\Dir1"/>
 </appSettings>
</variableAppSettings>

Teraz można to zaimplementować przy użyciu niestandardowych obiektów konfiguracyjnych, które zajmą się zastępowaniem zmiennych w czasie wykonywania.

JoshBerke
źródło
Nie widzę twojego xml w poście (dodaj wcięcie 5 znaków, aby móc publikować tagi xml - ostatnio miałem ten sam problem). Co to są „niestandardowe obiekty konfiguracyjne”? Wolę kodowanie zerowe, aby to osiągnąć, ponieważ zmiany w kodowaniu na tym etapie bardzo nas cofnęłyby.
DeeStackOverflow
Konfiguracja niestandardowa zdecydowanie obejmuje [proste] kodowanie. Ale IMHO to zawsze najlepsza opcja. Prawie nigdy nie używam appSettings, wolę zamiast tego tworzyć niestandardową konfigurację dla każdego projektu.
Portman
3

Zwykle piszę klasę statyczną z właściwościami dostępu do każdego z ustawień mojego pliku web.config.

public static class ConfigManager 
{
    public static string MyBaseDir
    {
        return ConfigurationManager.AppSettings["MyBaseDir"].toString();
    }

    public static string Dir1
    {
        return MyBaseDir + ConfigurationManager.AppSettings["Dir1"].toString();
    }

}

Zwykle wykonuję również konwersje typów, gdy jest to wymagane w tej klasie. Pozwala na wpisanie dostępu do konfiguracji, a jeśli zmienią się ustawienia, możesz je edytować tylko w jednym miejscu.

Zwykle zastąpienie ustawień tą klasą jest stosunkowo łatwe i zapewnia znacznie większą łatwość konserwacji.

Jaskółka oknówka
źródło
3

Możesz użyć zmiennych środowiskowych w swoim app.configscenariuszu, który opisujesz

<configuration>
  <appSettings>
    <add key="Dir1" value="%MyBaseDir%\Dir1"/>
  </appSettings>
</configuration>

Następnie możesz łatwo uzyskać ścieżkę za pomocą:

var pathFromConfig = ConfigurationManager.AppSettings["Dir1"];
var expandedPath = Environment.ExpandEnvironmentVariables(pathFromConfig);
autocro
źródło
2

Wewnątrz <appSettings>możesz tworzyć klucze aplikacji,

<add key="KeyName" value="Keyvalue"/>

Później możesz uzyskać dostęp do tych wartości za pomocą:

ConfigurationManager.AppSettings["Keyname"]
Sergio
źródło
Aby użyć klasy ConfigurationManager, należy dodać odwołanie do System.Configuration i dodać instrukcję using dla System.Configuration (import w VB)
cjk
2
Wskazanie jest prawidłowe, ale nie jest odpowiedzią na zadane pytanie.
Michaël Carpentier
1

Sugerowałbym DslConfig . Dzięki DslConfig można używać hierarchicznych plików konfiguracyjnych z Global Config, Config na hosta serwera do konfiguracji na aplikację na każdym hoście serwera (patrz AppSpike).
Jeśli jest to dla Ciebie
zbyt skomplikowane, możesz po prostu użyć globalnego pliku konfiguracyjnego Variables.var Wystarczy skonfigurować w Varibales.var

baseDir = "C:\MyBase"
Var["MyBaseDir"] = baseDir
Var["Dir1"] = baseDir + "\Dir1"
Var["Dir2"] = baseDir + "\Dir2"

I pobierz wartości konfiguracyjne za pomocą

Configuration config = new DslConfig.BooDslConfiguration()
config.GetVariable<string>("MyBaseDir")
config.GetVariable<string>("Dir1")
config.GetVariable<string>("Dir2")
Johannes
źródło
0

Myślę, że nie można deklarować i używać zmiennych do definiowania kluczy appSettings w pliku konfiguracyjnym. Zawsze zarządzałem konkatenacjami w kodzie, tak jak Ty.

Michaël Carpentier
źródło
0

Trochę zmagam się z tym, co chcesz, ale możesz dodać plik nadpisania do ustawień aplikacji, a następnie ustawić ten plik nadpisania dla każdego środowiska.

<appSettings file="..\OverrideSettings.config">
Andrew Barrett
źródło
0

Do wdrażania produktów, w których musimy skonfigurować wiele elementów o podobnych wartościach, używamy małych aplikacji konsolowych, które odczytują XML i aktualizują je na podstawie przekazanych parametrów. Są one następnie wywoływane przez instalator po tym, jak poprosił użytkownika o wymagana informacja.

cjk
źródło
0

Poleciłbym zastosować rozwiązanie Matta Hamsmitha. Jeśli jest to problem do zaimplementowania, to dlaczego nie utworzyć metody rozszerzenia, która implementuje to w tle w klasie AppSettings?

Coś jak:

    public static string GetValue(this NameValueCollection settings, string key)
    {

    }

Wewnątrz metody przeszukujesz DictionaryInfoConfigSection przy użyciu Linq i zwracasz wartość z pasującym kluczem. Musisz jednak zaktualizować plik konfiguracyjny do czegoś podobnego:

<appSettings>
  <DirectoryMappings>
    <DirectoryMap key="MyBaseDir" value="C:\MyBase" />
    <DirectoryMap key="Dir1" value="[MyBaseDir]\Dir1"/>
    <DirectoryMap key="Dir2" value="[MyBaseDir]\Dir2"/>
  </DirectoryMappings>
</appSettings>
Bogaty
źródło
0

Wymyśliłem takie rozwiązanie:

  1. W aplikacji Settings.settings zdefiniowałem zmienną ConfigurationBase (z type = string Scope = Application)
  2. Wprowadziłem zmienną do atrybutów docelowych w Settings.settings, wszystkie te atrybuty musiały być ustawione na Scope = User
  3. W pliku app.xaml.cs odczytałem wartość, jeśli plik ConfigurationBase
  4. W pliku app.xaml.cs zamieniłem wszystkie zmienne na wartość ConfigurationBase. Aby zastąpić wartości w czasie wykonywania, atrybuty musiały być ustawione na Scopr = User

Nie jestem zadowolony z tego rozwiązania, ponieważ muszę ręcznie zmienić wszystkie atrybuty, jeśli dodam nowy, muszę to uwzględnić w pliku app.xaml.cs.

Tutaj fragment kodu z App.xaml.cs:

string configBase = Settings.Default.ConfigurationBase;
Settings.Default.CommonOutput_Directory = Settings.Default.CommonOutput_Directory.Replace("${ConfigurationBase}", configBase);

AKTUALIZACJA

Właśnie znalazłem ulepszenie (ponownie fragment kodu z pliku app.xaml.cs):

string configBase = Settings.Default.ConfigurationBase;

foreach (SettingsProperty settingsProperty in Settings.Default.Properties)
{
    if (!settingsProperty.IsReadOnly && settings.Default[settingsProperty.Name] is string)
    {
        Settings.Default[settingsProperty.Name] = ((string)Settings.Default[settingsProperty.Name]).Replace("${ConfigurationBase}", configBase);
    }
}

Teraz zamiany działają dla wszystkich atrybutów w moich ustawieniach, które mają Type = string i Scope = User. Myślę, że podoba mi się ten sposób.

UPDATE2

Najwyraźniej ustawienie Scope = Application nie jest wymagane podczas uruchamiania właściwości.

anhoppe
źródło
0

Trzy możliwe rozwiązania

Wiem, że spóźnię się na imprezę, szukałem nowych rozwiązań problemu z ustawieniami zmiennej konfiguracji. Jest kilka odpowiedzi, które dotykają rozwiązań, z których korzystałem w przeszłości, ale większość z nich wydaje się nieco zawiła. Pomyślałem, że przyjrzę się moim starym rozwiązaniom i połączę wdrożenia, aby pomóc ludziom, którzy borykają się z tym samym problemem.

W tym przykładzie użyłem następującego ustawienia aplikacji w aplikacji konsoli:

<appSettings>
    <add key="EnvironmentVariableExample" value="%BaseDir%\bin"/>
    <add key="StaticClassExample" value="bin"/>
    <add key="InterpollationExample" value="{0}bin"/>
  </appSettings>

1. Użyj zmiennych środowiskowych

Myślę, że odpowiedź autocro autocro dotknęła tego. Po prostu wykonuję implementację, która powinna wystarczyć podczas budowania lub debugowania bez konieczności zamykania Visual Studio. Korzystałem z tego rozwiązania kiedyś ...

  • Utwórz zdarzenie przed kompilacją, które będzie używać zmiennych programu MSBuild

    Ostrzeżenie: użyj zmiennej, której nie da się łatwo zastąpić, więc użyj nazwy swojego projektu lub czegoś podobnego do nazwy zmiennej.

    SETX BaseDir "$(ProjectDir)"

  • Resetuj zmienne; używając czegoś podobnego do następującego:

    Odśwież zmienne środowiskowe po przepełnieniu stosu

  • Użyj ustawienia w swoim kodzie:

'

private void Test_Environment_Variables()
{
    string BaseDir = ConfigurationManager.AppSettings["EnvironmentVariableExample"];
    string ExpandedPath = Environment.ExpandEnvironmentVariables(BaseDir).Replace("\"", ""); //The function addes a " at the end of the variable
    Console.WriteLine($"From within the C# Console Application {ExpandedPath}");
}

'

2. Użyj interpolacji ciągów:

  • Użyj funkcji string.Format ()

`

private void Test_Interpollation()
{
    string ConfigPath = ConfigurationManager.AppSettings["InterpollationExample"];
    string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"..\..\"));
    string ExpandedPath = string.Format(ConfigPath, SolutionPath.ToString());
    Console.WriteLine($"Using old interpollation {ExpandedPath}");
}

`

3. Używając klasy statycznej, jest to rozwiązanie, którego najczęściej używam.

  • Implementacja

`

private void Test_Static_Class()
{
    Console.WriteLine($"Using a static config class {Configuration.BinPath}");
}

`

  • Klasa statyczna

`

static class Configuration
{
    public static string BinPath
    {
        get
        {
            string ConfigPath = ConfigurationManager.AppSettings["StaticClassExample"];
            string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"..\..\"));
            return SolutionPath + ConfigPath;
        }
    }
}

`

Kod projektu:

App.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
  <appSettings>
    <add key="EnvironmentVariableExample" value="%BaseDir%\bin"/>
    <add key="StaticClassExample" value="bin"/>
    <add key="InterpollationExample" value="{0}bin"/>
  </appSettings>
</configuration>

Program.cs

using System;
using System.Configuration;
using System.IO;

namespace ConfigInterpollation
{
    class Program
    {
        static void Main(string[] args)
        {
            new Console_Tests().Run_Tests();
            Console.WriteLine("Press enter to exit");
            Console.ReadLine();
        }        
    }

    internal class Console_Tests
    {
        public void Run_Tests()
        {
            Test_Environment_Variables();
            Test_Interpollation();
            Test_Static_Class();
        }
        private void Test_Environment_Variables()
        {
            string ConfigPath = ConfigurationManager.AppSettings["EnvironmentVariableExample"];
            string ExpandedPath = Environment.ExpandEnvironmentVariables(ConfigPath).Replace("\"", "");
            Console.WriteLine($"Using environment variables {ExpandedPath}");
        }

        private void Test_Interpollation()
        {
            string ConfigPath = ConfigurationManager.AppSettings["InterpollationExample"];
            string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"..\..\"));
            string ExpandedPath = string.Format(ConfigPath, SolutionPath.ToString());
            Console.WriteLine($"Using interpollation {ExpandedPath}");
        }

        private void Test_Static_Class()
        {
            Console.WriteLine($"Using a static config class {Configuration.BinPath}");
        }
    }

    static class Configuration
    {
        public static string BinPath
        {
            get
            {
                string ConfigPath = ConfigurationManager.AppSettings["StaticClassExample"];
                string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"..\..\"));
                return SolutionPath + ConfigPath;
            }
        }
    }
}

Wydarzenie przed kompilacją:

Ustawienia projektu -> Tworzenie wydarzeń

StormChild
źródło