![]() |
Web Development »
ASP.NET »
Howto
Intermediate
License: The Code Project Open License (CPOL)
Creating a custom intelligent configuration fileBy mazong1123An article on providing a tool to help web developers to create more flexible web applications. |
C# (C# 2.0, C# 3.0), XML, .NET (.NET 2.0, .NET 3.0, .NET 3.5), ASP.NET, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||


In our web applications, we always maintain some fixed information such as page titles, page URLs, web site banner text, common settings etc. Storing such information in the web.config file is a good idea. However, retrieving these information from the web.config file is not easy. Also, there is no intelligence at all, so developers should be very carful to make sure the value they typed is actually the same as in the web.config file. In this article, I'll demonstrate how to create a configuration tool which provides a new way to create a custom, intelligent config file.
This article is for developers who want to:
Step 1: Create a code generator.
The first thing to do is to create a code generator class.
/// <summary>
/// Custom code generator.
/// It will retrieve all the namespaces,classes and property from
/// the configuration file(xml file)
/// </summary>
public class ConfigCodeGenerator
{
//...
}
There are four key methods:
BuildNameSpace BuildAllClasses AddPropertyToClass GenerateUnitCode Implement the AddPropertyToClass method to add the property specified in the "Property" attribute of the "Class" node in the config file:
/// <summary>
/// Add properties to existed class
/// </summary>
/// <param name="objEntity">Existed class to add properties(reference)</param>
/// <param name="xmlndLs">Xml nodes which hold all properties </param>
/// <returns>True - success False - failed</returns>
private Boolean AddPropertyToClass(ref CodeTypeDeclaration objEntity,
XmlNodeList xmlndLs)
{
try
{
if ( (objEntity != null) && (xmlndLs != null) && (xmlndLs.Count > 0))
{
foreach (XmlNode xmlnd in xmlndLs)
{
if(xmlnd.Name == "Property")
{
// Get field type
CodeTypeReference objFieldType = new CodeTypeReference(
GetPropertyType(xmlnd.Attributes["type"].Value));
// Get property name then lower it and add "_"
// in front of the string to make the field name
String strPropertyName = xmlnd.Attributes["name"].Value;
String strFieldName = "_" + strPropertyName.ToLower();
// Get default value of the field
Object objValue = GetFieldDefaultValue(xmlnd.Attributes["value"].Value,
xmlnd.Attributes["type"].Value);
// Get comments
String strComment = String.Empty;
if (xmlnd.Attributes["comment"] != null)
{
strComment = xmlnd.Attributes["comment"].Value;
}
// Generate field
CodeMemberField objField = new CodeMemberField();
objField.Attributes = MemberAttributes.Private | MemberAttributes.Static;
objField.Name = strFieldName;
objField.Type = objFieldType;
objField.InitExpression = new CodePrimitiveExpression(objValue); // ???ֵ
// Generate property
CodeMemberProperty objProperty = new CodeMemberProperty();
objProperty.Attributes = MemberAttributes.Public | MemberAttributes.Static;
objProperty.Name = strPropertyName;
objProperty.Type = objFieldType;
objProperty.GetStatements.Add(new CodeSnippetStatement("return " +
strFieldName + ";"));
objProperty.Comments.Add(new CodeCommentStatement("<summary>", true));
objProperty.Comments.Add(new CodeCommentStatement(strComment, true));
objProperty.Comments.Add(new CodeCommentStatement("</summary>", true));
// Add field and property to class
objEntity.Members.Add(objField);
objEntity.Members.Add(objProperty);
}
}
return true;
}
return false;
}
catch(Exception ex)
{
throw new Exception(ex.Message + "\r\nFailed to add properties to class");
}
}
Implement BuildAllClasses to create the classes which are specified in the "Class" nodes in the config file:
/// <summary>
/// Recursive search all the nodes in the xml file
/// to find the useful elements
/// </summary>
/// <param name="xmlndRoot">Root of the xml file</param>
/// <param name="objTopEntity">The topmost class in the unit code</param>
private void BuildAllClasses(XmlNode xmlndRoot, ref CodeTypeDeclaration objTopEntity)
{
foreach (XmlNode xmlnd in xmlndRoot.ChildNodes)
{
if (xmlnd.Name == "Class")
{
CodeTypeDeclaration objSubEntity = null;
// add child class
if(xmlnd.Attributes["comment"] != null)
{
objSubEntity = BuildEmptyClass(xmlnd.Attributes["name"].Value,
xmlnd.Attributes["comment"].Value);
}
else
{
objSubEntity = BuildEmptyClass(xmlnd.Attributes["name"].Value);
}
objTopEntity.Members.Add(objSubEntity);
BuildAllClasses(xmlnd, ref objSubEntity);
}
else if (xmlnd.Name == "Property")
{
// add property
AddPropertyToClass(ref objTopEntity, xmlndRoot.ChildNodes);
break;
}
}
}
Implement BuildNameSpace to create the only namespace specified in root node in the config file:
/// <summary>
/// Build namespace
/// </summary>
/// <param name="xmlConfigFile">configuration file</param>
/// <returns>the generated namespace</returns>
private CodeNamespace BuildNameSpace(XmlDocument xmlConfigFile)
{
try
{
// The root is really the 2nd child node in the xml file cause the 1st one
// is ""
XmlNode xmlndRoot = xmlConfigFile.ChildNodes[1];
// Build namespace use the root name
CodeNamespace objNameSpace = new CodeNamespace(xmlndRoot.Name);
// The topmost class
CodeTypeDeclaration objTopEntity = null;
if(xmlndRoot.Attributes["comment"] != null)
{
objTopEntity = BuildEmptyClass(xmlndRoot.Name,
xmlndRoot.Attributes["comment"].Value);
objNameSpace.Comments.Add(new CodeCommentStatement("<summary>", true));
objNameSpace.Comments.Add(new
CodeCommentStatement(xmlndRoot.Attributes["comment"].Value, true));
objNameSpace.Comments.Add(new CodeCommentStatement("</summary>", true));
}
else
{
objTopEntity = BuildEmptyClass(xmlndRoot.Name);
}
// Build all classes in the namespace
BuildAllClasses(xmlndRoot, ref objTopEntity);
// Add the topmost class to the namespace
// Notice: objTopEntity already include all the subclasses and properties
objNameSpace.Types.Add(objTopEntity);
return objNameSpace;
}
catch(Exception ex)
{
throw new Exception(ex.Message + " Failed when execute BuildNameSpace");
}
}
Finally, call BuildNameSpace in GenerateUnitCode:
/// <summary>
/// Generate unit code
/// </summary>
/// <param name="xmlConfigFile">xml file to be parsed</param>
/// <returns></returns>
public CodeCompileUnit GenerateUnitCode(XmlDocument xmlConfigFile)
{
if (xmlConfigFile == null)
{
throw new ArgumentNullException("configex");
}
// Build the only namespace
CodeNamespace objNameSpace = BuildNameSpace(xmlConfigFile);
// Generate the unit code
CodeCompileUnit objCompileUnit = new CodeCompileUnit();
objCompileUnit.Namespaces.Add(objNameSpace);
return objCompileUnit;
}
Step 2: Inherit from BuildProvider and override the GenerateCode method to add a custom code unit:
/// <summary>
/// Custom config provider
/// </summary>
[PermissionSet(SecurityAction.Demand, Unrestricted = true)]
public class ConfigProvider : BuildProvider
{
public ConfigProvider()
{
}
/// <summary>
/// Override this method to generate own unit code
/// </summary>
/// <param name="assemblyBuilder">Assembly builder provided by the base class</param>
public override void GenerateCode(AssemblyBuilder assemblyBuilder)
{
XmlDocument configFile = new XmlDocument();
try
{
using (Stream file = VirtualPathProvider.OpenFile(this.VirtualPath))
{
configFile.Load(file);
}
}
catch
{
throw;
}
// Create a code generator to generate the whole code
ConfigCodeGenerator objCodeGenerator = new ConfigCodeGenerator();
// Generate the unit code and add it to the assembly builder
assemblyBuilder.AddCodeCompileUnit(this,
objCodeGenerator.GenerateUnitCode(configFile));
}
}
Here are the steps:
<buildProviders>
<add extension=".configex" type="ConfigurationEx.ConfigProvider"/>
</buildProviders>
<?xml version="1.0" encoding="utf-8" ?>
<WebsiteData xmlns="http://mazong1123.ys168.com/configex.xsd"
comment="Custom website settings">
<Class name="PageUrlGroup" comment="All page urls in this website">
<Property name="MainPage" value="~/Default.aspx"
type="string" comment="Main page url"/>
<Property name="SecondPage" value="~/SecondPage.aspx"
type="string" comment="Second page url"/>
</Class>
<Class name="PageTitles" comment="Titles displayed in the brower">
<Property name="MainPage" value="ConfigurationEx Test Page"
type="string" comment="Main page title"/>
<Property name="SecondPage" value="Second Page"
type="string" comment="Second page title"/>
</Class>
<Class name="ButtonText" comment="Collection of button text">
<Property name="ToSecondPage" value="Go to second page" type="string"/>
<Property name="ToMainPage" value="Back to main page" type="string"/>
</Class>
<Class name="PageInfomation" comment="Collection of page infomation">
<Property name="Title" value="Thanks to use ConfigurationEx!"
type="string" comment="Main page title"/>
<Property name="Banner" value="This is a banner infomation from custom config file"
type="string" comment="Banner infomation"/>
</Class>
</WebsiteData>
protected void Page_Load(object sender, EventArgs e)
{
if ( !IsPostBack )
{
// Initialize page data
Title = WebsiteData.WebsiteData.PageTitles.MainPage;
lbTitle.Text = WebsiteData.WebsiteData.PageInfomation.Title;
lbBanner.Text = WebsiteData.WebsiteData.PageInfomation.Banner;
lbBtnToSecondPage.PostBackUrl =
WebsiteData.WebsiteData.PageUrlGroup.SecondPage;
lbBtnToSecondPage.Text =
WebsiteData.WebsiteData.ButtonText.ToSecondPage;
}
}
Since I have to maintain a lot of information like page URLs, I try to find an easy way to reduce the work time. That's why this tool was created. Now, it's possible to write my own code without any hard coding and not having to remember the actual setting values. Just enjoy it!
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 2 Feb 2009 Editor: Smitha Vijayan |
Copyright 2009 by mazong1123 Everything else Copyright © CodeProject, 1999-2009 Web10 | Advertise on the Code Project |