.Net - Configuration-System

    • Allgemein

      .Net - Configuration-System

      Beispiel einer App.config

      XML-Quellcode

      1. <?xml version="1.0"?>
      2. <configuration >
      3. <configSections>
      4. <section name="myconfig4" type="MyProggi.XSection, MyProggi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      5. <sectionGroup name="userSettings"
      6. type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
      7. <section name="MyProggi.My.MySettings" allowExeDefinition="MachineToLocalUser" requirePermission="false"
      8. type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      9. </sectionGroup>
      10. <sectionGroup name="applicationSettings"
      11. type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
      12. <section name="MyProggi.My.MySettings" requirePermission="false"
      13. type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      14. </sectionGroup>
      15. </configSections>
      16. <!-- XSection: if existent file set read its content - otherwise read inner-Xml -->
      17. <myconfig4 file="MyConfig.xml" />
      18. <userSettings>
      19. <MyProggi.My.MySettings>
      20. <setting name="Setting1" serializeAs="String">
      21. <value>Setting1Value</value>
      22. </setting>
      23. </MyProggi.My.MySettings>
      24. </userSettings>
      25. <applicationSettings>
      26. <MyProggi.My.MySettings>
      27. <setting name="Setting2" serializeAs="String">
      28. <value>Setting2Value</value>
      29. </setting>
      30. </MyProggi.My.MySettings>
      31. </applicationSettings>
      32. <connectionStrings>
      33. <add name="ConfigProtector.My.MySettings.MyConString1" connectionString="ConConnectionString1" />
      34. <add name="ConfigProtector.My.MySettings.MyConString2" connectionString="ConConnectionString2" />
      35. </connectionStrings>
      36. <appSettings file="MyAppSettings2.xml">
      37. <!-- inexistent file gets ignored; More settings can also be added here -->
      38. <add key="key2" value="value2" />
      39. </appSettings>
      40. </configuration>
      (Achtung! Erklärungen und Code-Snippets beziehen sich teilweise auf diese Config)

      .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 erbt
      sieht 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:

      XML-Quellcode

      1. <connectionStrings configSource="ConStrings.config"/>

      und die ConStrings.config, auf die verwiesen wird, muss eine gültige connectionStrings-Section enthalten, und sonst nichts:

      XML-Quellcode

      1. <?xml version="1.0" encoding="utf-8" ?>
      2. <connectionStrings>
      3. <add name="connection1" connectionString="ConConnectionString1" providerName="Provider1" />
      4. <add name="connection2" connectionString="ConConnectionString2" providerName="Provider2" />
      5. </connectionStrings>





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

      VB.NET-Quellcode

      1. ''' <summary>die XRoot-Property stellt die Section als XElement bereit. Kann auch Nothing sein, wenn die config keinen Eintrag enthält</summary>
      2. Public Class XSection : Inherits ConfigurationSection
      3. Public XRoot As XElement
      4. Protected Overrides Sub DeserializeSection(reader As XmlReader)
      5. XRoot = XElement.Load(reader)
      6. Dim xFile = XRoot.@file
      7. If Not String.IsNullOrWhiteSpace(xFile) Then
      8. With New FileInfo(xFile)
      9. If .Exists Then XRoot = XElement.Load(.FullName)
      10. End With
      11. End If
      12. End Sub
      13. 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“ ()