in this article, a simple web based editor is built for the web.config file of asp.net. this article will cover the basics of the new web.config file structure, as well as helping the reader to understand the configuration file hierarchy of the .net environment. it will also demonstrate how the xmldocument object and its methods are used to simply load, modify, and write out xml from a .aspx page . the final code download is a simple web configuration file editor that allows a user to change the configuration settings of a web application through another web application, without needing to have explicit knowledge of the web.config file and it's format. code design motivation
in order to edit the web.config file through the web, the page performing the configuration work needs to be in a separate web application or application domain from the web application for which it is changing the configuration settings. this is because of the way asp.net deals with configuration file changes. recall from the explanation above, that when asp.net detects changes in the web.config file, it reconfigures itself according to those changes, but allows current requests to finish using the current configuration settings. given this, it follows that the file performing the configuration would benefit by not changing along with the new configuration settings, as it becomes hard to detect the new changes. this is not to say that the simple web configuration tool will only work in it's own application domain, but results may appear non deterministic at times, and therefore a bit more difficult to debug.
figure 1 illustrates the environment of the web configuration tool as it was developed and tested. a web application with the alias " webconfig " points to a directory containing the configuration editing aspx code. a second web application with the alias " testsite " points to a directory representing the production web application to be configured. the webconfigedit.aspx code will load, edit, and write out the web.config for the test site using the simple web based interface of the aspx page. webconfigedit.aspx control modes
the webconfigedit.aspx page works in two modes, normal and postback. in normal mode, the code simply initializes form elements with the current settings in the web.config file and displays them as choices to the user. in postback mode, the code reads in the user selections and applies those selections to the web.config file. the following code illustrates the overall control flow of the webconfigedit.aspx code.
void page_load(object sender, eventargs e){
. . .
//code omitted for clarity
. . .
//if the page is in postback mode, update the config file
if(page.ispostback){
updatepageitems(ref objxml);
updatetraceitems(ref objxml);
updatecustomerrors(ref objxml);
objxml.save(config_file);
}else{
//else use the config file to initialize the form elements
initializepageitems(objxml);
initializetraceitems(objxml);
initializecustomerrors(objxml);
}
} loading web.config
loading the web.config file from as aspx page is a very straightforward operation that relies on the fact that the configuration file is a well-formed xml document. the xmldocument class in the system.xml namespace has a simple load(string) method that allows an xml document to be parsed and loaded into memory given a file path as a string. this allows the web.config file to be loaded into an xml dom that the code can now search and edit easily using the dom interface. the following c# code in the page_load method loads the web.config xml file into an xmldocument object.
//path to the configuration file for the target web application
const string config_file = "c://testsite//web.config";
//create the xml document object
xmldocument objxml;
objxml = new xmldocument();
//load the xml document representing the web.config file
objxml.load(config_file);
of course i agree that a hard-coded path to the configuration file is not ideal. storing the path in an appsetting section of a web.config file would be more appropriate, but a string constant suits the purpose of this article just fine. initializing the asp.net form elements
as mentioned above, when the page is first loaded it needs to display the current values of the web.config file using form elements. asp.net server controls are used for this as they allow consistent server-side coding throughout the application. the html code snippet below illustrates the use of the asp:checkbox control and the asp.net textbox control in the webconfigedit.aspx page.
<asp:checkbox id="chkbuffer" runat="server" text="buffering" />
<asp:textbox id="txtrequestlimit" runat="server" size="3" />
these controls can now be referenced from the c# code in the page_load method. to initialize a configuration element, the element's xml node first needs to be located within the xml dom. this is done using the selectsinglenode(string) method of the xmldocument class. this method returns an xml node object given an xpath statement as a string, without having to iterate through the dom tree looking for a desired node. the c# code snippet below shows a method from webconfigedit.aspx that will initialize the page configuration items.
void initializepageitems(xmldocument objxml){
//get the pages xml element
xmlelement pages = (xmlelement)objxml.selectsinglenode("//pages");
//set the check boxes according to the current file selections
if(pages.getattribute("buffer") == "true"){
chkbuffer.checked = true;
}
if(pages.getattribute("enableviewstate") == "true"){
chkenableviewstate.checked = true;
}
}
note that the code is using the less efficient, but more flexible //pages xpath shortcut. this informs the xpath engine to search for any page node at all levels of the xml document instead of supplying the explicit path. when an xpath statement does not define the fully qualified path, the xpath engine needs to check more nodes than it does for the fully qualified path. this is done for code clarity only. for more information about xpath see http://msdn.microsoft.com/library/default.asp?url=/library/en- http://msdn.microsoft.com/library/default.asp?url=/library/en-us/xmlsdk30/htm/xmconxpath.asp. using the selectsinglenode method, a node representing the <pages> configuration settings is returned. this is then explicitly cast as an xmlelement object to take advantage of the getattribute(string) method of that class. this method allows the lookup of a value for an xml node attribute by name. the code can now initialize the checkbox or textbox element using the value of the attributes for the given <pages> configuration sections. figure 2 below shows the rendered page for an example web.config file. updating the web.config file
in postback mode, the code needs to read the posted data choices from the form elements and use those to update the appropriate values of the web.config file. to accomplish this, once again the xml data of the web.config file needs to be loaded into xml dom as before. once the web.config information is in the dom, we can utilize form values given by the user to update the dom elements. below is a c# code snippet that performs this work for the <pages> configuration section.
void updatepageitems(ref xmldocument objxml){
//get the pages xml element
xmlelement pages = (xmlelement)objxml.selectsinglenode("//pages");
//set the fields according to the user selections
if(chkbuffer.checked){
pages.setattribute("buffer", "true");
}
else{
pages.setattribute("buffer", "false");
}
if(chkenableviewstate.checked){
pages.setattribute("enableviewstate", "true");
}
else{
pages.setattribute("enableviewstate", "false");
}
}
the first item to understand is the use of the ref keyword in the parameter list of the method as shown below.
void updatepageitems(ref xmldocument objxml)
this indicates that the parameter is to be passed by reference instead of by value, which is the default in c#. the code needs to pass this item by reference to avoid creating deep copies of the xml object each time a method is called to update the xml. remember, the xml object was created in the page_load method using the web.config data. passing this to a method by value would give the method it's own local copy of the xmldocument object. any edits to this object would not be reflected in the original xmldocument object created in page_load. when this object is then saved back to disk, none of the edited information will be included. passing the object by reference allows the method to modify the original object and have those modifications persist after the method exits.
once the method has a reference to the xml dom, it needs to locate the desired node to be edited. like the initializing code, this also uses the selectsinglenode method, casting the return as an xmlelement object.
xmlelement pages = (xmlelement)objxml.selectsinglenode("//pages");
now, instead of the code checking the xml element to determine the setting of the form element, the code simply uses the value of the form element to edit the configuration file settings. this is done using the setattribute(string,string) method of the xmlelement object. this method takes a string indicating the name of the attribute, and another string indication the value for that attribute. saving the web.config file
once the web.config file's xml dom representation has been modified, it needs to written out again to disk for the changes to be implemented by asp.net. this is done using the save(string) method of the xmldocument object as the c# code snippet below illustrates.
objxml.save(config_file);
the save method simply takes a string parameter representing a file path, and serializes the xml out to that location. in this case, the code uses the same file path as was used to read in the web.config file, thus overwriting the configuration file for the web application.