Czy w plikach app.config
lub 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).
c#
variables
web-config
app-config
DeeStackOverflow
źródło
źródło
Odpowiedzi:
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
ConnectionStrings
sekcji znajduje się rodzaj symbolu zastępczego o nazwie „| DataDirectory |”. Może mógłbyś rzucić okiem, co tam pracuje ...Oto fragment z
machine.config
pokazania tego:<connectionStrings> <add name="LocalSqlServer" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient" /> </connectionStrings>
źródło
Nieco bardziej skomplikowaną, ale znacznie bardziej elastyczną alternatywą jest utworzenie klasy reprezentującej sekcję konfiguracji. W Twoim
app.config
/web.config
pliku 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.config
zmiennych, 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"
źródło
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;"
źródło
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
źródło
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.
źródło
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.
źródło
Możesz użyć zmiennych środowiskowych w swoim
app.config
scenariuszu, 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);
źródło
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"]
źródło
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")
źródło
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.
źródło
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">
źródło
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.
źródło
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>
źródło
Wymyśliłem takie rozwiązanie:
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.
źródło
Trzy możliwe rozwiązania
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
Utwórz zdarzenie przed kompilacją, które będzie używać zmiennych programu MSBuild
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ń
źródło