Benutzer mit den meisten Antworten
Wie mit Konfigurationsdaten umgehen?

Frage
-
Meine Anwendung benötigt einige wenige Konfigurationsdaten, auf die sie beim Start lesend, aber im Ablauf durchaus auch schreibend zugreifen können muss. So wenig, daß sich dafür keine Datenbank zu erstellen lohnt. Früher hätte ich eine INI genommen, jetzt würde ich eher ein XML-Datei verwenden.
Im Prinzip kann ich mir die Fragen nach dem Parsen einer XML-Datei und rausschreiben derselben auch im einzelnen selbst beantworten, aber mal in die Runde gefragt, auf Neulingsniveau:
1. Ich füge meine Projekt eine leere XML-Datei zu (<?xml version="1.0" encoding="utf-8" ?>) Soweit gut, da kann man beliebiges XML reinschreiben. Unter Eigenschaften habe ich eine Menge Dateieigenschaften, die mir nicht so klar werden.
Was ich versuche herauszufinden: Wie sorge ich dafür, daß diese XML-Datei nicht in die Assembly eincompiliert wird, sondern als gesonderte Datei liegenbleibt? Welche Bedeutung hat speziell die Eigenschaft "Buildvorgang"?2. Wie lese ich zur Laufzeit am einfachsten eine XML-Datei in ein paar Variablen, eine Collection o.ä. ein? Mal angenommen ich hätte als Grundidee einige XML-Elemente der Form
<object type="serialportsettings"> <field1>value1</field1>
<field2>value2</field2>
<field3>value3</field3> <field4>value4</field4>
</object>3. Wie ermittle ich zur Laufzeit in C# Systempfade, wie z.B. den Pfad zu Anwendungsdaten, in den ich solche XML-Konfigurationsdaten installieren würde, um meine XML-Datei dort dann zu finden, lesen und schreiben zu können? Zielsysteme sind Vista oder neuer.
Ziel ist wie immer bei der Art von Konfiugurationsdateien, daß es einigermaßen gut administrativ manuell änderbar sein sollte, also eben eher INI oder XML, kein Binärformat.
Zielframework ist .NET Framework 4
Olaf Doschke
(Setmics)- Bearbeitet Olaf Doschke Mittwoch, 27. Februar 2013 13:59
Antworten
-
Hallo Olaf,
zu 1.-2.) zuerst sollte man gucken, ob man nicht mit der Standard-Anwendungskonfiguration klarkommt. In dem Falle wäre es mit dem Hinzufügen einer App.Config getan und die Einstellungen kann man über die Projekteigenschaften erledigen. Die kann man bei Bedarf um eigene Elemente erweitern, siehe u. a. ConfigurationManager.
Will man seine XML Konfigurationen selbst machen gibt es mehrere Wege. Ein grundlegendes Verständnis vom Aufbau von Xml Dokumenten sollte man dafür schon haben.
Am einfachsten in der Handhabung dürfte dabei die Xml-Serialisierung sein. Dazu erstellt man mit Attributen versehene Klassen, die als Xml serialisiert und deserialisiert werden können.
Mehr Handarbeit (und fehleranfälliger) ist bei LINQ To Xml oder ein XmlDocument notwendig, da man die Daten entweder direkt extrahieren oder in Klassen übertragen muss.
Eingebettet werden Dateien nur wenn sie als eingebettete Ressource gekennzeichnet wird, siehe Visual Studio Dateieigenschaften.
zu 3.) Die Pfade zu den Standard-Verzeichnissen erhält man über Environment.GetFolderPath, siehe u. a. Hat der Ordner C:\Program Files\Common Files immer SchreibLeserechte ?
Welches Verzeichnis für welchen Zweck beschreibt recht gut: Where Should I Write Program Data Instead of Program Files?
Das Startverzeichnis der Anwendung erhält man (bei Windows Forms) über Application.StartupPath für WPF (oder auch alternativ bei Windows Forms) siehe:
http://stackoverflow.com/questions/938421/getting-the-applications-directory-from-a-wpf-applicationDas ist jetzt nur ein grober Überblick, wie üblich stecken die Teufelchen in den Details ;)
Gruß Elmar
- Als Antwort markiert Olaf Doschke Donnerstag, 28. Februar 2013 09:28
Alle Antworten
-
Hallo Olaf,
zu 1.-2.) zuerst sollte man gucken, ob man nicht mit der Standard-Anwendungskonfiguration klarkommt. In dem Falle wäre es mit dem Hinzufügen einer App.Config getan und die Einstellungen kann man über die Projekteigenschaften erledigen. Die kann man bei Bedarf um eigene Elemente erweitern, siehe u. a. ConfigurationManager.
Will man seine XML Konfigurationen selbst machen gibt es mehrere Wege. Ein grundlegendes Verständnis vom Aufbau von Xml Dokumenten sollte man dafür schon haben.
Am einfachsten in der Handhabung dürfte dabei die Xml-Serialisierung sein. Dazu erstellt man mit Attributen versehene Klassen, die als Xml serialisiert und deserialisiert werden können.
Mehr Handarbeit (und fehleranfälliger) ist bei LINQ To Xml oder ein XmlDocument notwendig, da man die Daten entweder direkt extrahieren oder in Klassen übertragen muss.
Eingebettet werden Dateien nur wenn sie als eingebettete Ressource gekennzeichnet wird, siehe Visual Studio Dateieigenschaften.
zu 3.) Die Pfade zu den Standard-Verzeichnissen erhält man über Environment.GetFolderPath, siehe u. a. Hat der Ordner C:\Program Files\Common Files immer SchreibLeserechte ?
Welches Verzeichnis für welchen Zweck beschreibt recht gut: Where Should I Write Program Data Instead of Program Files?
Das Startverzeichnis der Anwendung erhält man (bei Windows Forms) über Application.StartupPath für WPF (oder auch alternativ bei Windows Forms) siehe:
http://stackoverflow.com/questions/938421/getting-the-applications-directory-from-a-wpf-applicationDas ist jetzt nur ein grober Überblick, wie üblich stecken die Teufelchen in den Details ;)
Gruß Elmar
- Als Antwort markiert Olaf Doschke Donnerstag, 28. Februar 2013 09:28
-
Hallo Olaf,
zu 1.-2.) zuerst sollte man gucken, ob man nicht mit der Standard-Anwendungskonfiguration klarkommt.
definitiv nicht, da die XML-Datei auf jeden fall zum Teil nicht statisch sein wird.
Will man seine XML Konfigurationen selbst machen gibt es mehrere Wege. Ein grundlegendes Verständnis vom Aufbau von Xml Dokumenten sollte man dafür schon haben.
Es könnte besser sein, aber ich habe Grundkenntnisse und Tools dafür.
Am einfachsten in der Handhabung dürfte dabei die Xml-Serialisierung sein. Dazu erstellt man mit Attributen versehene Klassen, die als Xml serialisiert und deserialisiert werden können.
In der Richtung werde ich mal schauen. Ansonsten hatte ich jetzt schon mit einigen DataTables und dereen ReadXML/WriteXML hantiert.
...Environment.GetFolderPath, siehe u. a.
Danke, welche Systempfade für welche Art von Daten ideal geeignet sind weiß ich dann schon.Ich nehme Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) und hänge da Company+Application dran.
Tschüß, Olaf.
Olaf Doschke (Setmics)
-
Hallo Olaf,
da dürfte ein Missverständnis vorliegen: Anwendungskonfigurationsdateien sind nicht statisch.
Es handelt sich dabei um Xml-Dateien und dort können Einstellungen verändert, Auflistungen erweitert und, wenn es sein muss, direkte Manipulationen via Xml vorgenommen werden.
Prinzipiell ist es ähnlich strukturiert wie die Xml Serialisierung, nur spezialisiert auf die Anforderungen der Konfiguration.
Was die anderen Wege angeht:
DataTables sind relativ unhandlich und sperrig und untypisiert. Besser greift man auf Standard-Klassen und -Auflistungen zurück.
Restriktionen ergeben sich vor allem aus der Ablage. Änderungen im Programm-Verzeichnis ist für normale Anwender nicht möglich und müssen in Benutzer- oder Anwendungsverzeichnissen gespeichert werden. Für Windows Forms kann man die Eigenschaften aus der Application-Klasse verwenden.
Gruß Elmar
-
Gut, allerdings hatte ich mir Deinen Verweis auf den ConfigurationManager angesehen und mich beim Lesen des Beispielcodes nach publicstaticclass UsingConfigurationManager davon verabschiedet. Sorry, das ist mir zuviel drum herum, nur um an eine Sektion in der App.config heranzukommen.
Eine oder zwei gesonderte XML-Dateien sind für meinen Zweck schon echt goldrichtig, auch im Hinblick auf die evtl. manuelle Editierbarkeit. Es wird im Normalfall einfach eine neue XML-Datei im Update kommen, aber im Ausnahmefall finde ich das config XML-Format nicht so prickelnd, um schnell eine Stelle zu finden, die ich ändern möchte.
Die Ansage über die nicht typisierten DataTables nehme ich mal auf, serialisierung konkreter Klassen/Objekte/Collections passt mir sowieso besser. In einem Teil der Daten wäre allerdings eine DataTable gar nicht verkehrt.
Mit den Systemverzeichnissen, UAC und Restriktionen kenne ich mich wie schon gesagt gut aus.
LocalSystem darf in CommonApplicationData schreiben, das dürfte sogar im Anwendungsverzeichnis schreiben. Ich versuche nur gerade, das ganze nicht nur speziell für diesen Fall zu programmieren, sondern etwas generischer.
Olaf Doschke (Setmics)
PS: Ich habe noch einen sehr hilfreichen Artikel dazu gefunden: https://www.google.de/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CDAQFjAA&url=http%3A%2F%2Fdownload.microsoft.com%2Fdownload%2F9%2F2%2F3%2F923D72FB-0076-49B6-96C4-AAC1C255A60E%2Fdotnetpro_4_03_anwendungsdaten_verwalten.pdf&ei=nZcvUc-3B8bZtQa7hYGwBQ&usg=AFQjCNGvQrDxXV_dVlLXWA0ajKH2cpd3Mg&bvm=bv.42965579,d.Yms- Bearbeitet Olaf Doschke Donnerstag, 28. Februar 2013 17:47
-
Wen's interessiert, was ich jetzt genutzt habe: XML Serialisierung von Collections.
Im Detail noch nicht generisch, aber für meine Zwecke reicht's:
namespace GlobalDefinitions { ... public class SerialPortSetting { public int BaudRate {get; set;} public int DataBits { get; set; } public StopBits StopBits {get; set;} public Parity Parity {get; set;} public SerialPortSetting() { // zur Serialisierbarkeit dieser Klasse muss ein parameterloser Konstruktor bestehen! } public SerialPortSetting(int BaudRate, int DataBits, StopBits StopBits, Parity Parity) { this.BaudRate = BaudRate; this.DataBits = DataBits; this.StopBits = StopBits; this.Parity = Parity; } } } namespace MyNamespace { ... internal class PortSettings : IDisposable { private Collection<SerialPortSetting> MyPortSettings = new Collection<SerialPortSetting>(); public void ReadSettings() { string AppdataPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), @"Company\Product\"); if (!Directory.Exists(AppdataPath)) { Directory.CreateDirectory(AppdataPath); } if (File.Exists(AppdataPath + "portsettings.xml")) { // read settings from CommonAppData XmlSerializer x = new XmlSerializer(typeof(Collection<SerialPortSetting>)); TextReader reader= new StreamReader(AppdataPath + "portsettings.xml"); MyPortSettings = (Collection<SerialPortSetting>)x.Deserialize(reader); reader.Close(); } else { // create initial settings MyPortSettings.Add(new SerialPortSetting(4800, 7, StopBits.One, Parity.None)); MyPortSettings.Add(new SerialPortSetting(4800, 8, StopBits.One, Parity.None)); MyPortSettings.Add(new SerialPortSetting(4800, 7, StopBits.One, Parity.Odd)); MyPortSettings.Add(new SerialPortSetting(4800, 8, StopBits.One, Parity.Odd)); ... XmlSerializer x = new XmlSerializer(typeof(Collection<SerialPortSetting>)); TextWriter writer = new StreamWriter(AppdataPath + "portsettings.xml"); x.Serialize(writer, MyPortSettings); writer.Close(); } } } }
Ich finde es recht elegant das initiale XML einfach auch per Serialisierung zu erzeugen, dann macht man keine Fehler.
Und das resultierende XML sieht dann etwa so aus:
<?xml version="1.0" encoding="utf-8"?> <ArrayOfSerialPortSetting xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <SerialPortSetting> <BaudRate>4800</BaudRate> <DataBits>7</DataBits> <StopBits>One</StopBits> <Parity>None</Parity> </SerialPortSetting> <SerialPortSetting> <BaudRate>4800</BaudRate> <DataBits>8</DataBits> <StopBits>One</StopBits> <Parity>None</Parity> </SerialPortSetting> <SerialPortSetting> <BaudRate>4800</BaudRate> <DataBits>7</DataBits> <StopBits>One</StopBits> <Parity>Odd</Parity> </SerialPortSetting> <SerialPortSetting> <BaudRate>4800</BaudRate> <DataBits>8</DataBits> <StopBits>One</StopBits> <Parity>Odd</Parity> </SerialPortSetting> ... </ArrayOfSerialPortSetting>
Vielleicht hilft's ja jemand anderem auch als Anregung.
Olaf Doschke (Setmics)
- Bearbeitet Olaf Doschke Freitag, 1. März 2013 15:07
-
Hallo Olaf,
eine Kleinigkeit: Seit .NET 2.0 sollte man Xml Reader und Writer über die Create-Methoden erzeugen, siehe Erstellen von XML-Readern und Erstellen von XmlWritern.
Gruß Elmar
-
Danke, solche Hinweise kann ich gut gebrauchen. Beim zusammensammeln von Beispielcode findet man seltener angaben über den Stand der Technik (wobei ich nur Deinem Rat gefolgt bin hin zu http://msdn.microsoft.com/de-de/library/bb552803.aspx)
Für dumme (wie mich) nachgefragt: Du meinst jeweils statt StreamReader/StreamWriter, richtig?
Olaf Doschke (Setmics)
- Bearbeitet Olaf Doschke Montag, 4. März 2013 11:05
-
Hallo Olaf,
ja, anstatt der StreamReader/-Writer, denn über die Settings wird (nicht nur) das Encoding gesteuert.
Typischer Luftcode fürs Schreiben:
XmlSerializer serializer = new XmlSerializer(typeof(Klasse)); var settings = new XmlWriterSettings() { Indent = true }; using (var writer = XmlWriter.Create(DateiName, settings)) { serializer.Serialize(writer, instanzDerKlasse); }
Typischer Luftcode fürs Lesen:
XmlSerializer serializer = new XmlSerializer(typeof(Klasse)); var settings = new XmlReaderSettings(); using (var reader = XmlReader.Create(DateiName, settings)) { var instanzDerKlasse = (Klasse)serializer.Deserialize(reader); }
Wobei man bei speziellen Anforderungen die jeweiligen Settings anpassen würde.
Gruß Elmar
-
Danke.
>Wobei man bei speziellen Anforderungen die jeweiligen Settings anpassen würde.
z.B. wenn man gegen ein XSD Schema validieren möchte?
Ich habe mir mittlerweile mittels xsd.exe am VS2010 Commandprompt aus den Klassen der Assembly ein Schema generieren lassen und dann im zweiten Schritt aus dem Schema nochmal Klassendefinitionen. Das führt dann zum Verlust des Konstruktors und zur Redefinition der System.IO.Ports Enumerations StopBit und Parity, aber abgesehen von diesen "Fehlern" noch zu einigen Attribut-Annotationen.
Ich sehe, da ist wie immer sehr viel mehr möglich, als ich gerade gebrauchen kann, aber gut zu wissen.
Olaf Doschke (Setmics)