XML-Quellcode
- <?xml version="1.0"?>
- <configuration >
- <configSections>
- <section name="myconfig4" type="MyProggi.XSection, MyProggi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
- <sectionGroup name="userSettings"
- type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
- <section name="MyProggi.My.MySettings" allowExeDefinition="MachineToLocalUser" requirePermission="false"
- type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
- </sectionGroup>
- <sectionGroup name="applicationSettings"
- type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
- <section name="MyProggi.My.MySettings" requirePermission="false"
- type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
- </sectionGroup>
- </configSections>
- <!-- XSection: if existent file set read its content - otherwise read inner-Xml -->
- <myconfig4 file="MyConfig.xml" />
- <userSettings>
- <MyProggi.My.MySettings>
- <setting name="Setting1" serializeAs="String">
- <value>Setting1Value</value>
- </setting>
- </MyProggi.My.MySettings>
- </userSettings>
- <applicationSettings>
- <MyProggi.My.MySettings>
- <setting name="Setting2" serializeAs="String">
- <value>Setting2Value</value>
- </setting>
- </MyProggi.My.MySettings>
- </applicationSettings>
- <connectionStrings>
- <add name="ConfigProtector.My.MySettings.MyConString1" connectionString="ConConnectionString1" />
- <add name="ConfigProtector.My.MySettings.MyConString2" connectionString="ConConnectionString2" />
- </connectionStrings>
- <appSettings file="MyAppSettings2.xml">
- <!-- inexistent file gets ignored; More settings can also be added here -->
- <add key="key2" value="value2" />
- </appSettings>
- </configuration>
.Net-config ist ein Xml-Dialekt.
Generell gilt ja für Xml, dasses strukturiert Informationen bereitstellt, für maschinelle Verarbeitung vorgesehen.
Welche Information nun was bedeutet für das verarbeitende Programm ist im Xml-Standard natürlich nicht festgelegt, sondern solch ist Sache des verarbeitenden Programms.
Somit - wenn immer ein Proggi Xml auswertet, und dabei spezifische Regeln befolgt - spricht man von einem Xml-Dialekt.
Dieses Tut nun will die Regeln benennen, denen ein .Net-Programm folgt, wenn es "seine" .config-Datei einliest.
1) Es wird nur eine(!!) .config eingelesen
Und zwar diejenige mit Namen
<NameDesStartProzesses>.exe.config
, der im Anwendungsverzeichnis liegt.Das kann verwirrend sein, weil üblicherweise durchaus mehrere .config-Files in den Sources oder Binaries herumfahren.
Schon wenn man mit
F5
aus der IDE heraus kompiliert und startet (BeispielName MyProggi
), so kopiert die IDE die app.config
unter zwei Namen ins Ausgabeverzeichniss:a)
MyProggi.exe.config
, b) MyProggi.vshost.exe.config
Und angewendet wird letztere .config, denn bei einem Testlauf mit
F5
startet die IDE nicht die kompilierte exe, sondern den Hosting-Prozess: MyProggi.vshost.exe
.Startet man hingegen
MyProggi.exe
per Doppelklick im Explorer, so wird dafür erstere eingelesen, die MyProggi.exe.config
.(PS.: @EaranMaleasi merkt an, dass seit VS2017 die IDE keine
MyProggi.vshost.exe.config
mehr generiert.)2) configSections müssen "angekündigt" sein
Eine Config zerfällt in sog. configSections - das sind alle direkten <configuration/>-Kind-Elemente, ausser: <configSections/>.
<configSections/> ist der "Ankündigungs-Knoten", im Beispiel sieht man 3 <section/>s angekündigt, und 2 <sectionGroup/>s.
Wäre etwa <myconfig4/> nicht angekündigt, so würde der Eintrag in Zeile#13 beim Programmstart einen Fehler verursachen.
3) Die Ankündigung muss einen Datentyp angeben, der von
System.Configuration.ConfigurationSection
erbtsieht man ja im Beispiel - das type-Attribut gibt einen gültigen Datentyp an, und zwar in "assembly-qualified" Schreibweise (welche oft etwas länglich ausfällt)
Bei Initialisierung erstellt das KonfigurationsSystem ein Objekt dieses Typs und lässt es das Xml der entsprechenden configSection auslesen. Somit sind die Regeln, die innerhalb einer Section gelten, dem KonfigurationsSystem selbst eiglich unbekannt, und wie nun
<myConfig4/>
ausgewertet wird, ist allein Sache der XSection-Klasse, die ich dafür gecodet habe.Also was eingangs gesagt wurde: Das .config-Format ist ein Xml-Dialekt, dessen Regeln im KonfigurationsSystem der .Net-Runtime implementiert sind - das gilt im kleinen auch für jede einzelne configSection: jede Section hat ihr eigenes Regelwerk, welches im Datentyp implementiert ist, der bei ihrer Ankündigung angegeben wird.
Code kann mit
ConfigurationManager.GetSection(string)
sich das jeweilige Section-Objekt holen, welches (hoffentlich) die eingelesenen Section-Daten in typsicherer und leicht verwendbarer Form bereitstellt.Tatsächlich braucht man solch extrem selten. Aber es ist sicherlich gut zu wissen, was im Hintergrund so vor sich geht, etwa wenn man ein Logging-Framework einbindet, und dessen Einstellungen inne app.config zu konfigurieren hat.
4) machine.config - das Default-config-File
Dem aufmerksamen Leser ist vlt. aufgefallen, dass die letzten beiden Sections meiner Config nicht in <configSections/> angekündigt sind.
Dieses ist deshalb zulässig, weil in den Tiefen der Microsoft.NET-Framework-Installation existiert eine
\machine.config
, in der bereits eine grosse Anzahl von configSections vor-angekündigt sind, wie zB auch die <connectionStrings/>
und die <appSettings/>
.\machine.config
liegt als Konfiguration also jedem .Net-Proggi zugrunde, und die spezifische MyProggi.exe.config
ist eine Erweiterung derselben (bzw. eine Einschränkung, sie kann nämlich auch Einträge der \machine.config
in gewissem Rahmen aufheben.)5)
configSource
- Einbinden von "Config-File-Trabanten"In Entwicklerteams kann sinnvoll sein, die eine oder andere ConfigSection in eine Extra-Datei auszulagern - die dann etwa nicht unter Source-Control steht.
So arbeiten alle Entwickler mit derselben app.Config, nur etwa die connectionsStrings-Section mag ausgelagert sein, sodass der Entwickler über den config-Trabanten etwa eine abweichende, Test-Datenbank hinterlegen kann.
Die entsprechende configSection sähe dann so aus:
und die ConStrings.config, auf die verwiesen wird, muss eine gültige connectionStrings-Section enthalten, und sonst nichts:
So, das waren die fünf config-Grundregeln.
Nu gehe ich noch auf zwei ConfigSections ein. Warum grade auf die, und auf die 10000 anderen, die's auch noch gibt, nicht - kann ich eiglich garnicht schlüssig begründen.
Section-Beispiel
<appSettings/>
Wie gesagt,
<appSettings/>
sind bereits in machine.config
vorangekündigt, und wie obiges Beispiel zeigt, gibt es hier eine "Trabanten-Alternative": Nämlich man kann statt configSource="xy"
auch file="xy"
angeben. Der Unterschied ist, dass bei anderen ConfigSections entweder-oder gilt, also entweder die Section ist ausgelagert, oder sie ist includiert.Bei appSettings.file gilt sowohl-als auch, also ausgelagerte appSettings kann man damit includieren zusätzlich zu den in der section geaddeten settings.
Am besten man guckt sich im ObjectBrowser die
AppSettingsSection
-Klasse an. Das ist die Klasse, zu der ein <appSettings/>
-Abschnitt deserialisiert wird.Übrigens
<applicationSettings/>
und <appSettings/>
sind komplett andere Dinge!Man kann sich auch gerne mal die
ConnectionStringsSection
-Klasse angucken, und mit AppSettingsSection
vergleichen - solches Browsen im ObjectBrowser lässt einen das durchdachte System besser verstehen, finde ich.Meine
XSection
-KlasseVB.NET-Quellcode
- ''' <summary>die XRoot-Property stellt die Section als XElement bereit. Kann auch Nothing sein, wenn die config keinen Eintrag enthält</summary>
- Public Class XSection : Inherits ConfigurationSection
- Public XRoot As XElement
- Protected Overrides Sub DeserializeSection(reader As XmlReader)
- XRoot = XElement.Load(reader)
- Dim xFile = XRoot.@file
- If Not String.IsNullOrWhiteSpace(xFile) Then
- With New FileInfo(xFile)
- If .Exists Then XRoot = XElement.Load(.FullName)
- End With
- End If
- End Sub
- End Class
Die Klasse veröffentlicht nur ein Feld, nämlich
XRoot
. Und das stellt nichts anderes dar als den Xml-Inhalt der Section.Das ist etwas anders als im Sinne des Erfinders, weil eiglich sollen die
ConfigurationSection
-Erben die Config auslesen und in zweckgebundene Datenmodelle überführen - eine ConnectionStringsSection
stellt zb eine Liste von ConnectionStringSettings bereit jedes mit Name, Provider und ConnectionString.Meine
XSection
stellt kein son festgelegtes Datenmodell dar. Damit ist sie universal verwendbar, bietet aber nur einen Auswertungs-Zwischenschritt, nämlich ein XElement, was selbst wiederum auf alle möglichen Weisen auswertbar ist.Mir kommts auch nur auf die
Overrides Sub DeserializeSection
an, die bei der Initialisierung des ConfigSystems das Section-Xml als Reader übergeben bekommt.Diese Überschreibung ist der Mechanismus, mit dem sich selbstgebastelte ConfigSection-Erben in die Initialisierung des ConfigSystems einklinken sollten, um jeweils "ihr" Xml auszulesen und davon die eigenen zweckgebundenen Properties zu befüllen.
Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „ErfinderDesRades“ ()