Poniższy artykuł pochodzi z serii Przygotowań do egzaminu 70-536.
Przechowywanie ustawień aplikacji, czy ustawień połączenia z bazą danych (ang. connection string), to częsty wymóg. Zapisywanie ich “na sztywno” w kodzie aplikacji jest złą praktyką. Na szczęście .NET Framework udostępnia nam zestaw klas, które ułatwiają przechowywanie tych ustawień w specjalnych plikach XML. Mamy dwa główne typy takich plików:
- Globalny plik Machine.config, który jest wspólny dla wszystkich aplikacji korzystających z .NET Framework,
- Plik <Application_Name>.config skojarzony z konkretną aplikacją.
Zapisywanie ustawień
W naszym projekcie należy dodać referencję (opcja Add Reference…), do biblioteki System.Configuration, która znajduje się w zakładce .NET. Następni należy dodać odpowiednią dyrektywę using:
1: using System.Configuration;
Przykładowy program:
1: Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
2: config.AppSettings.Settings.Add("MyKey", "MyValue");
3: // Zapisz plik konfiguracyjny
4: config.Save(ConfigurationSaveMode.Modified);
- Wywołanie ConfigurationManager.OpenExeConfiguration utworzy instancję klasy Configuration,
- Configuration.Add doda klucz i wartość do ustawień,
- Wywołanie Configuration.Save zapisze ustawienia do pliku .config.
Uwaga: Po zbudowaniu aplikacji i uruchomieniu z poziomu Visual Studio, plik ustawień nie zostanie utworzony. VS nie korzysta ze standardowego pliku .config. Zbudowany program należy odpalić ręcznie z katalogu projektu.
Wygenerowany plik XML ustawień wygląda nastęująco:
1: <?xml version="1.0" encoding="utf-8"?>
2: <configuration>
3: <appSettings>
4: <add key="MyKey" value="MyValue" />
5: </appSettings>
6: </configuration>
Plik ten możemy zmodyfikować samodzielnie, np. za pomocą programu Notatnik. Wewnątrz znacznika appSettings dodajmy nowe ustawienie:
1: <add key="Ala" value="ma kota" />
Zadanie: Zakładamy, że nasz program zawiera poniższy kod. Który z kluczy zostanie zapisany?
1: ConfigurationManager.AppSettings.Set("Key1", "Value1");
2: Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
3: config.AppSettings.Settings.Add("Key2", "Value2");
4: config.Save((ConfigurationSaveMode.Modified));
5: config.AppSettings.Settings.Add("Key3", "Value3");
Zapisany zostanie jedynie Key2, ponieważ po jego dodaniu wywołana jest metoda config.Save. Dla pierwszego klucz (Key1) wywołanie ConfigurationManager.AppSettings.Set nie zapisze ustawień na stałe. Po kluczu nr 3 (Key3) nie ma wywołania metody Save, więc ustawienie nie zostanie zapisane.
Odczyt ustawień
Ustawienia aplikacji możemy odczytać korzystając ze statycznej kolekcji ConfigurationManager.AppSettings. Zakładając, że chcemy wypisać wszystkie ustawienia:
1: for (int i = 0; i < ConfigurationManager.AppSettings.Count; i++)
2: {
3: Console.WriteLine("{0}: {1}",
4: ConfigurationManager.AppSettings.AllKeys[i],
5: ConfigurationManager.AppSettings[i]);
6: }
Wypisze:
1: MyKey: MyValue
2: Ala: ma kota
Możemy także wyciągnąć wartość konkretnego ustawienia podając klucz:
1: Console.WriteLine(ConfigurationManager.AppSettings["Ala"]); // ma kota
Przechowywanie ustawień połączenia z bazą danych
Connection string definiuje połączenie naszej aplikacji z bazą danych. Przechowywanie tych ustawień w pliku pozwala łatwo nam łatwo je zmienić w przypadku zmiany parametrów połączenia.
Dodajmy przykładowe ustawienie do naszego pliku .config (w obrębie znacznika appSettings):
1: <connectionStrings>
2: <add name="LocalSqlServer"
3: connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
4: providerName="System.Data.SqlClient" />
5: </connectionStrings>
Mamy skonfigurowany connection string pod kluczem LocalSqlServer, chcemy dostać się do niego z poziomu naszego kodu.W kolekcji ConfigurationManager.ConnectionStrings podajemy klucz, aby pobrać ustawienia połączenia.
1: Console.WriteLine(ConfigurationManager.ConnectionStrings["LocalSqlServer"].ConnectionString);
Wypisanie jest oczywiście najprostszym przykładem. Jeśli mielibyśmy więcej połączeń możemy wypisać informacje o nich wszystkich:
1: ConnectionStringSettingsCollection connections = ConfigurationManager.ConnectionStrings;
2: foreach (ConnectionStringSettings connection in connections)
3: {
4: Console.WriteLine("Name: {0}", connection.Name);
5: Console.WriteLine("Connection string: {0}",
6: connection.ConnectionString);
7: Console.WriteLine("Provider: {0}", connection.ProviderName);
8: Console.WriteLine("Source: {0}",
9: connection.ElementInformation.Source);
10: }
Ustawienia maszyny (Machine Configuration Settings)
Zwykle nie ma takiej potrzeby, ale gdybyśmy chcieli dostać się do globalnych ustawień należy wywołać metodę ConfigurationManager.OpenMachineConfiguration. W wyniku jej wywołania otrzymamy obiekt Configuration, który zawiera sekcje z różnymi ustawieniami.
1: Configuration machineSettings = ConfigurationManager.OpenMachineConfiguration();
2: ProtectedConfigurationSection pcs = (ProtectedConfigurationSection)machineSettings.GetSection("configProtectedData");
3: Console.WriteLine(pcs.DefaultProvider);
4: Console.WriteLine(pcs.Providers["DataProtectionConfigurationProvider"].Parameters["description"]);
Powyższy przykład:
- Pobiera ustawienia maszyny,
- Pobiera konfigurację sekcji configProtectedData, rzutuje go na obiekt ProtectedConfigurationSection,
- Wyświetl parametry zwróconego obiektu,
- Wyświetl parametr wybranego providera.
Tworzenie własnych sekcji
Własne sekcje możemy utworzyć na dwa sposoby:
- Implementacja interfejsu IConfigurationSectionHandler,
- Dziedziczenie po ConfigurationSection.
Pierwsza metoda oznaczona jest jako przestarzała. Dziedziczenie po ConfigurationSection jest preferowaną metodą tworzenia własnych sekcji w .NET Framework, począwszy od wersji 2.0. Poniżej przykład z Training Kit:
1: namespace ConfigApp
2: {
3: public class MyHandler : ConfigurationSection
4: {
5: public MyHandler()
6: {
7: }
8: [ConfigurationProperty("lastUser", DefaultValue = "User",
9: IsRequired = true)]
10: [StringValidator(InvalidCharacters =
11: "~!@#$%^&*()[]{}/;'\"|\\",
12: MinLength = 1, MaxLength = 60)]
13: public string LastUser
14: {
15: get
16: {
17: return (string)this["lastUser"];
18: }
19: set
20: {
21: this["lastUser"] = value;
22: }
23: }
24: [ConfigurationProperty("lastNumber")]
25: public int LastNumber
26: {
27: get
28: {
29: return (int)this["lastNumber"];
30: }
31: set
32: {
33: this["lastNumber"] = value;
34: }
35: }
36: }
37: class Program
38: {
39: static void Main(string[] args)
40: {
41: MyHandler settings =
42: (MyHandler)ConfigurationManager.GetSection(
43: "customSettings");
44: Console.WriteLine(settings.LastUser);
45: Console.WriteLine(settings.LastNumber);
46: }
47: }
48: }
Zadanie: Chcemy przeczytać ustawienia z sekcji pliku .config aplikacji. Co możemy zrobić?
- Utworzyć klasę, która dziedziczy po ConfigurationSection – zalecana metoda,
- Zdefiniować sekcję <configSection> w pliku .config aplikacji,
- Utworzyć własną sekcję w sekcji <configuration> pliku .config aplikacji.
Istnieje jeszcze opcja dziedziczenia po interfejsie IConfigurationSectionHandler. Nie jest to jednak metoda zalecana.
Kolejny artykuł z serii to 70-536:Configuring the .NET Framework