XML-based configuration file access interface design and implementation (1)
table of Contents
Summary
Profile structure
Implementation of XMLConfigReader class
XMLConfigReader class
Summary
During program development, some program settings / usage are often stored. Since this information and program setting / use, it is quite independent with the program, so it is impossible to hardcode into the program. In this When we choose to use XML-based profiles to store. Microsoft's .NET Framework provides a series of readsTTings, such as read-based files, such as the appSettings provided by System.Configuration namespace. But this namespace is provided Classs can only read the configuration file, can not be set. So here, we implement a class XMLConfigReader / XMLConfigWriter based on our XML-based profile.
Configuration file structure
In order to achieve compatibility with the original, systematic profile, we choose to use the structure similar to the .config file. Examples are as follows:
XML Version = "1.0" encoding = "UTF-8"?>
appsettings>
Suckbasesettings>
configure>
All the information to be set is placed in sub-nodes of the Configuration node (such as AppSettings / Sockbasesettings) sub-node, which helps to categorize / unity of information different settings. Structure and system .config structure Basically similar. This makes it easy to convert this custom structure into .config files.
Implementation of XMLConfigReader class
The basic structure of the current file has been completed, and now the encoding is started, and XMLConfigReader is completed.
Since the configuration file is placed on the hard disk in the form of a file, this XMLConfigReader class gets the path to the file before parsing the XML file.
Public Class XmlconfigReader
{
PRIVATE STRING _FILEPATH;
Public XMLConfigReader (String filepath) {
_filepath = path.getfullpath (filepath) .toupper ();
}
}
Ok, now you can get the file path. Then it is parsing the configuration file. Here, we use the .NET Framework provided by the lightweight XMLTextReader in the system.xml namespace to make the configuration file. Corresponding XMLConfigReader The function is defined as follows:
Public String Process (String SectionName, String Key) {BOOL Inconfiguration = FALSE
BOOL INSECTION = FALSE;
String Values;
XMLTextReader Reader = New XMLTextReader (_FilePath);
While (Reader.Read ()) {
IF (reader.isstartElement ()) {
IF (reader.prefix == String.empty)
{
IF (reader.localname == "configuration")
{
Inconfiguration = true;
}
Else if (Inconfiguration == True) {
IF (reader.localname == sectionname) {
insection = true;
}
Else IF (INSECTION && Reader.LocalName == "add") {
IF (Reader.GetaTribute ("key") == null || reader.getattribute ("value") == null)
{
Throw new Exception (SectionName "Key or Value Is Null");
}
IF (Reader.GetaTribute ("key") == key) {
VALUES = Reader.GetaTRibute ("Value");
Break;
}
}
}
}
}
}
Reader.Close ();
Return Values;
}
The nodes in the XML file are traversed by XMLTextReader's READ () function. At the same time, it is first determined whether it belongs to the Configuration node, and then determines whether it belongs to the corresponding sectionName. Only when these two parts are set up, it is necessary to determine if it is the corresponding Key. If so, get it Value and return.
XMLConfigReader class
Ok, now you can read the configuration file through XMLConfigReader. Here we look at the actual code:
Public class testxmlconfigreader {
Public void getValues () {
XmlconfigReader Reader = New XMLConfigRead (@ "appconfig.xml");
String Temp;
// Get Appsettings UserName Value
Temp = Reader.Process ("Appsettings", "UserName");
// Get Sockbasesettings Serverip Value
Temp = Reader.Process ("SockBaseSettings", "Serverip");
}
}
to sum up
With the XMLConfigReader class, we can easily customize our own profile.
XML-based configuration file access interface design and implementation (2)
table of Contents
Summary
Implementation of XMLConfigWriter class
XMLConfigWriter class
Summary
During program development, some program settings / usage are often stored. Since this information and program setting / use, it is quite independent with the program, so it is impossible to hardcode into the program. In this When we choose to use XML-based profiles to store. Microsoft's .NET Framework provides a series of readsTTings, such as read-based files, such as the appSettings provided by System.Configuration namespace. But this namespace is provided Classs can only read the configuration file, can not be set. So here, we implements our own XML-based configuration file class XMLConfigReader / XMLConfigWriter.xmlconfigWriter class implementation
Since the configuration file is to be written, and it is possible to write more times. So we don't use light XMLTextWriter, using XMLDocument.xmldocument to modify all XML nodes in memory, only wait until the explicit call save function The XML file will only be saved. When there is a lot of modifications, performance is better.
Similarly, first implement XMLConfigWriter constructor
Public Class XmlconfigWriter
{
PRIVATE STRING _FILEPATH;
Private xmldocument doc;
Public XMLConfigWriter (String Filepath)
{
_filepath = path.getFullPath (FilePath);
Doc = new xmldocument ();
Doc.Load (_filepath);
}
}
By constructor, the path of the configuration file is passed in, and the XMLDocument's Load method is called, and this file is loaded into memory.
Here we use the XMLDocument class. It implements the W3C Document Object Model (DOM) Level 1 Core (Core Dom Level 2). The DOM is the memory of the XML document (cache) tree representation, allowing navigation and editing of the document. With XMLDocument, we can easily operate nodes in memory.
For the writing of the configuration file, there is three kinds, one is new inserted a node, one is the modification of the existing node, the last one is to delete an existing node. We first start with the insertion. The code is as follows :
Private XMLNode CreateXmlNode (String localname) {
Return Doc.createnode (XMLNodettype.element, localname, "");
}
Private XMlattribute Createxmlattribute (String LocalName) {
Return Doc.createAttribute ("", localname, "");
}
Public void addsection (string section) {
XMLNode Secnode = doc.selectsinglenode ("/ configuration /" section);
IF (secnode! = null) {
Return;
}
Doc.documentelement.Appendchild (seenode (section);
}
Public void addkey (string section, string key) {
XMLNode Secnode = doc.selectsinglenode ("/ configuration /" section);
IF (Doc.selectsinglenode ("/ configuration /" section "/ add [@ key = /" " key " / "]")! = NULL) {
Return;
}
XMLNode Chi = CreatexmlNode ("add");
Xmlattribute Att = Createxmlattribute ("Key");
ATT.VALUE = KEY;
Chi.attributes.Append (att);
ATT = CREATEXMLATTRIBUTE ("Value");
ATT.Value = Value;
Chi.attributes.Append (att);
Secnotore.Appendchild (Chi);
}
For the insertion of the profile, there are two situations, one is inserting a new section node (ie the node like Appsettings / Sockbasesettings), one is inserted into a new ADD node under the current section node. In the above code, For the operation of the insertion node, it is first to determine if this is the same name node, if it exists, if it exists, directly RETURN, avoid creating the same name node. However, due to the final use of the ADD node is different The Section node, so it is only judged that the node below the same section node cannot be the same name.
If there is no node of the same name, add this new (through the CreateXMLNode function) node to the DOC object via the secnode.appenild function. At the same time, for the Add Node, it will be KEY / VALUE through the CreateXmlattributes.appent function. The property is added to this node. In this way, the insertion operation is complete.
Then we will complete the delete operation. Delete operation directly gets the parent node of the target node directly through the XMLDocument, and then deletes it through the XMLNode.RemoveChild operation. The code is as follows:
Public void deletesection (String section) {
XMLNode Secnode = doc.selectsinglenode ("/ configuration /" section);
Doc.documentelement.removechild (secnode);
}
Public void deleteKey (String section, string key) {
XMLNode Secnode = doc.selectsinglenode ("/ configuration /" section "/ add [@ key = /" " key " / "]");
IF (secnode! = null)
{
SECNODE.PARENTNODE.RemoveChild (Secnode);
}
}
Now start modifying. For modification, the idea is like this, first pass the XMLDocument's SelectsingLenode search to see if there is a node that satisfies the condition. If not, it is directly returnis, if present, then two cases. For the Add node, just modify directly Its Value property. For the section node, it is deleted by traversing all the sub-nodes (add nodes) below, then remove this section node, reproduce a new node (this new node's Name is to set. Value), then assign all the child nodes to this new node. The code is as follows:
Public void modifysection (String oldsection, string newsection) {
XMLNode Secnode = doc.selectsinglenode ("/ configuration /" ilsection); xmlnodelist list = secnode.childnodes;
Doc.documentelement.removechild (secnode);
Secnode = doc.createnode (XMLNodetype.element, NewSection, "");
Foreach (XMLNode I in List) {
Secnotore.Appendchild (i);
}
Doc.documentelement.Appendchild (secnode);
}
Public void modifyKey (String Section, String Key, String Value) {
XMLNode Secnode = doc.selectsinglenode ("/ configuration /" section "/ add [@ key = /" " key " / "]");
IF (secnode! = null)
{
Secnotore.attributes ["Value"]. Value = Value;
}
}
Ok, insert, modify, and delete operations are now basically completed, but now it is only in memory, not to operate the actual files. At this time, we have to modify the memory via the XMLDocument.save function. A good XML file is written to the file. The code is as follows:
Public void save () {
Doc.save (_filepath);
}
Public void save (String filepath)
{
Doc.save (FilePath);
}
XMLConfigWriter class
How to use it is simple. You first generate an XMLConfigWriter object, pass the configuration file into the configuration file, and then operate through the add / modify / delete and other functions. The code is as follows:
XmlconfigWriter Writer = new xmlconfigwriter (@ "appconfig.xml");
Writer.addsection ("appsettings");
Writer.AddKey ("Appsettings", "Serverip", "Localhost");
Writer.ModifyKey ("Appsettings", "Serverip", "127.0.0.1");
Writer.ModifySECTION ("Appsettings", "SockBasetdings");
Writer.deleteKey ("SockBaseSettings", "Serverip");
Writer.Deletesection ("sockbasesettings");
Writer.save ();
to sum up
We learned to use XMLDocument by writing XMLConfigWriter.
XML-based configuration file access interface design and implementation (3)
table of Contents
Summary
Add cache support
Increase configuration file monitoring
Add configurationSettings class
Summary
In the two, we implemented the basic functions of XMLConfigReader and XMLConfigWriter. Because XMLConfigReader implemented, each request once, go to parse the configuration file once, the performance is very low. At the same time, we add a configurationSettings class. Used to call XMLConfigReader and XMLConfigWriter to make it the same as those in System.Configuration.
Add cache support
Since the implementation of XMLConfigReader is requested once, parsing the configuration file, and the information of the configuration file will be used in the program, which is obviously too low. Therefore, here is the cache. Cache, actually equivalent A static variable, is unique when running throughout the program, stores information in this variable through such a variable, and can directly get this information directly. Thus avoiding frequent parsing profiles. Here, we choose to use HashTable as a cache variable.
In MSDN, we can find a nameValueCollection (Key / Value key value) in the System.Configuration namespace. For easy use, we will also store the information after the configuration file is parsed in NameValueCollection .
After this definition, for the key in the HashTable set to the name of the section (AppSettings / Sockbasesetting, the value value is the object of the NameValueCollection class of all child nodes of this node.
Modify the code. Add a static HashTable variable to XmlConfigReader and modify the relevant function. Get the obtained information directly in this hashtable in the form of NameValueCollection.
Private static hashtable conftypes = new hashtable ();
PRIVATE STRING ROOTNAME;
Public void process () {
XMLTextReader Reader = New XMLTextReader (_FilePath);
While (Reader.Read ()) {
IF (reader.isstartElement ()) {
#Region Analyze the Files
IF (reader.prefix == String.empty)
{
IF (reader.localname == "configuration")
{
Inconfiguration = true;
}
Else if (Inconfiguration == True) {
IF (reader.localname == "add")
{
IF (Reader.GetaTribute ("key") == null || reader.getattribute ("value") == null)
{
Throw New Exception (rootname "key or value is null);
}
AddKey (Tables, Reader.GetaTribute ("Key"), Reader.GetaTribute ("Value");
}
Else
{
Rootname = reader.localname;
}
}
}
#ndregion
}
Else if (Reader.LocalName == "configuration") {
Inconfiguration = false;
}
}
Reader.Close ();
}
Private Void AddKey (String Key, String Value) {
NameValueCollection Collection;
IF (conftypes.containskey) {
Collection = (NameValueCollection) conftypes [rootname];
}
Else {
LOCK (confTypes.syncroot) {
Collection = new namevalueCollection (); conftypes.add (rootname);
}
}
Collection.Add (key, value);
}
In the above code, we modified the Process function. Change the original direct return result to call the AddKey function. By a class member rootname temporarily stores the current sectionName, add the obtained Key / Value to the Hashtable by addKey.
Now, after modification, you can't get directly to the value of the key we think to get. So we write a function again.
Public NameValueCollection getCollection (String Sectionname) {
IF (confTypes.containskey) {
Return (NameValueCollection) conftypes [sectionname];
}
Else {
Throw New Exception (Confname "Is Not Found In XmlConfiguration Files");
}
}
Here, we get the NameValueCollection collection of all child nodes of this node through sectionName. In this way, we can get the value we want.
Increase configuration file monitoring
The above code implements the cache of the configuration file. It greatly improves flexibility. But there is a problem, that is, if the configuration file is modified, this cache will not be updated automatically.
To solve this problem, we have to use FileSystemWatcher, to subscribe to file modification messages, and update the cache. Since this configuration file is added to the monitor file table before the first resolution, we modify XMLConfigReader, add one Static FileSystemWatcher, to save the object of the monitoring file, add a static BOOL value indicating whether you have modified. Remove the constructor, make the configuration file to the monitor list at the beginning. The code is as follows:
Private static filesystemWatcher Watch = new filesisness ();
Private static bool ismodify = true;
Public XMLConfigReader (String filepath) {
_filepath = path.getfullpath (filepath) .toupper ();
Watch.includesubdirectories = false;
Watch.path = path.getdirectoryName (FilePath);
Watch.notifyfilter = notifyfilters.size | NotifyFilters.lastwrite
Watch.filter = path.GetFileName (FilePath);
Watch.changed = new filesystemeventhandler (change_even);
Watch.enableraisingevents = true;
}
Since the file modification notification is implemented by an event mechanism, we also implement the character of the chane_even, modify the value of IsModify through this function.
PRIVATE VOID CHANGE_EVEN (Object Sender, FileSystemEventArgs E) {
Ismodify = true;
}
This is done for the code for the monitoring of the profile, and now it is time to modify our getCollection code.
The modified code is as follows:
Public NameValueCollection getCollection (String Sectionname) {
IF (ismodify) {
LOCK (confTypes.syncroot) {
.
}
ismodify = false;
}
IF (confTypes.containskey) {
Return (NameValueCollection) conftypes [sectionname];
}
Else {
Throw New Exception (Confname "Is Not Found In XmlConfiguration Files");
}
}
By now, the code of the entire XMLConfigReader has been completed, and the monitoring of the file can be realized to dynamically modify the value in the cache.
Add configurationSettings class
To make it easy to use, we add a CONFIGURATIONSETTINGS class to use his usage and the usage of the class in the System.Configuration namespace. The code is defined as follows:
Public Class ConfigurationSettings: XMLConfigWriter
{
Private static string _filepath = @ "appconfig.xml";
Public Static String DefaultFilePath
Private static xmlconfigreader reader;
{
Get {return _filepath;
Set {_filepath = path.getFullPath (value);
}
Public Static NameValueCollection Appsettings
{
Get {
IF (Reader == NULL) {
Reader = New XmlconfigRead (DefaultFilePath);
}
Return Reader.getCollection ("appsettings");
}
}
Public Static NameValueCollection getConfig (String Sectionname) {
Get {
IF (Reader == NULL) {
Reader = New XmlconfigRead (DefaultFilePath);
}
Return Reader.getCollection (SectionName);
}
}
In this way, it is the same as the system comes with the system.
to sum up