Page 1 of 1

Using XML To Share Constants Across Multiple Web Applications in C#

#1 PsychoCoder   User is offline

  • Google.Sucks.Init(true);
  • member icon

Reputation: 1659
  • View blog
  • Posts: 19,853
  • Joined: 26-July 07

Post icon  Posted 10 January 2008 - 01:52 AM

Welcome to my tutorial on how to use XML to Share constants across multiple projects. Not too long ago I was looking for a way to have a single class and XML file that could be shared across multiple Web Applications, asides from the web.config. The reason I chose to not use the we.config for this is 3 fold:

  • Changes to the web.config will cause the Web Application to restart, slowing down the time it takes to roll out the changes
  • All values stored in the web.config are stored as strings, making it impossible to store different and more complex data types
  • Going this route required a recompile and re-deploy, quite the hassle if you ask me


So I knew there had to be a way to pull this off, using a regular XML file doesn't require a recompilation when values are changed, it can be stored in a central location, and XML is now the wave of the future. Now that I have figured out my medium for holding the constants, I needed a way to read the constants file. The preferred way would be to load the entire XML document into cache, thus not having to use the System.IO Namespace when I needed to retrieve a constant value.

The first item I want to look at is the XML document itself. It needs to be a standalone document, like so:

<?xml version="1.0" encoding="utf-8" standalone="yes" ?>



In this document you can store any items you want to share across multiple Web Applications such as:

  • DB Connection Strings
  • Results per page (for paging)
  • Email server information


And the list goes on. But what if you need multiple versions of the same item in different applications, how would you go about ding that you ask? Well the best way I found (and most efficient way) is to implement versioning for the document itself. To do this I added a single tag <APP_VERSION></APP_VERSION>, then when the application first loads this version is retrieved and help in cache. From there you add a version attribute to the items that need different versions for different applications. Lets taker a look at a sample XML application that does just this:


<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
   <applicationSettings>
	  <!-- Version -->
	  <APP_VERSION>1</APP_VERSION>

	  <!--Database settings-->
	  <!-- Notice the "version" attribute used-->
	  <DB_CONN version="1">server=yourServer;uid=username;pwd=password;database=database</DB_CONN>
	  <DB_CONN version="2">server=yourOtherServer;uid=username;pwd=password;database=database</DB_CONN>

	  <!--EMail Seerver-->
	  <EMAIL_SERVER version="1">email.server.com</EMAIL_SERVER>
	  <EMAIL_SERVER version="2">email.server2.com</EMAIL_SERVER>

	   <!--Directories -->
	  <LOG_FILE version="1">c:\inetpub\wwwroot\logfiles\</LOG_FILE>
	  <LOG_FILE version="2">c:\inetpub\wwwroot\logfiles\ApplicationName\</LOG_FILE>
   </applicationSettings>




Remember, the file above is an example file, you can add as many items as you wish, I just added a couple items to show how it can be utilized. If you have items (like the ones above) that need to be different than another application, simply add the version attribute. Now onto the class that make all this work.

As with any class you need to import your Namespaces, this prevents you from having to type System.IO.File.Exists. The Namespaces we need for this class are:


using System;
using System.Web.Caching;
using System.Xml;
using System.Threading;
using System.Text.RegularExpressions;




Next, we have our Global variables, these are global as they are referenced throughout the entire class file, so here are our globals needed:


#region Variables
	protected static internal Cache constantsCache = System.Web.HttpRuntime.Cache;
	private static string constantsFile = "ApplicationConstantsFile";
	public static string constantsFileName = Regex.Replace(System.AppDomain.CurrentDomain.BaseDirectory, "//", "\\") + "applicationConstants.config";
	private static XmlDocument xmlDoc = new XmlDocument();
	private static string identifier = "constant";
	private static string constKey = "cacheDependencyKey";
	private static string tempVersion = "THIS_VERSION";
	private static string thisVersion;
#endregion



NOTE: I always separate the different sections of my code in #region...#endregion blocks for ease of finding code in a large class, plus it makes it easier to organize the class

Next are the class ClassConstructors. we have a single constructor in this class, in this constructor we clear out a cache key item, then set the version number. No recompilation required this way:


#region Constructors
	/// <summary>
	/// This is the only constructor we have for this class. This
	/// constructor clears the version each time it is initialized
	/// then re-reads the config file so it can reset the current version
	/// </summary>
	public Manager()
	{
		//clear out the version number with a new instantiation
		//cleany out any old values
		ClearCache();
		//set the version numer
		thisVersion = RetrieveConstants(ref tempVersion).ToString();
	}
#endregion



Ok, we have our variables, we have an instantiation on our object, time to start working with the constants page, and introduce the Cache Class, which gives the ability to hold constants in cache, allowing us to check the cache for the value of a key, so no need to use the System.IO Namespace, Cache is much faster.

When the object is first instantiated it calls RetrieveConstants, this is a method for retrieving key values from the XML file, based on the version, which is what is first created. This method requires a ref parameter.

Pass the key to it, it then checks the Cache object created in our global variables for that key. If it doesn't find the key it calls the PopulateCache method to add the item to the Cache object. If it finds the key it returns the key to the calling method:


#region RetrieveConstants
	/// <summary>
	/// method to retrieve a specified key from the cache
	/// </summary>
	/// <param name="key">key value to retrieve</param>
	/// <returns></returns>
	public object RetrieveConstants(ref string key)
	{
		//create an object to hold the value (if it exists)
		object obj;
		//make sure the static identifier plus the provided
		//key isnt null
		if ((constantsCache[identifier + key] != null))
		{
			//set our object to its value
			obj = (object)constantsCache[identifier + key];
		}
		else  
		{
			//value isnt in the cache, now we need to retrieve it and put it into the cache
			obj = RetrieveConstantsFromFile(ref key);

			//make sure the cache item (obj) isnt null
			if ((obj != null))
			{
				//since its not null we need to add it to our cache
				//so we dont have to hit the file again if we need the value
				if (constantsCache[constKey] == null)
				{
					constantsCache.Insert(constKey, DateTime.Now);
				}
				constantsCache.Insert(identifier + key, obj, new CacheDependency(constantsFileName));
			}
		}
		return obj;
	}
#endregion




RetrieveConstantsFromFile is the method called, this is the method that does most of the work. The method first makes sure the current cache isn't empty, if it is it calls the PopulateCache Method which will add that item to the cache since it doesn't exist. If it is found, then we start parsing the XML config file looking for the key specified.

For this we use the GetElementByTagName Method of the XML Document Class. After the search it checks to see if the number of nodes returned is greater than zero (meaning the key was found).

If only a single node is returned that means there is no version attribute on that constant, otherwise we use the SelectSingleNode and using XPath looking for the version attribute. Here is the RetrieveConstantsFromFile Method:


#region RetrieveConstantsFromFile
	private object RetrieveConstantsFromFile(ref string key)
	{
		object obj = 0;
		if (constantsCache[constantsFile] == null)
		{
			PopulateCache();
		}

		//Attempt to find the element given the "key" for that tag
		XmlNodeList nodeList = xmlDoc.GetElementsByTagName(key);

		//If the key is found, the element list will have a count greater than
		//zero and we retrieve the value of the tag...
		if (nodeList.Count > 0)
		{
			XmlNode node;

			//If there is only 1 element in the list, then the element doesn't
			//have a version number and can be returned as is.
			if (nodeList.Count == 1)
			{
				node = nodeList.Item(0);
			}

			//If there is more than 1 element in the list, find the element for
			//the version of the application
			else
			{
				node = xmlDoc.SelectSingleNode("descendant::" + key + "[@version=" + thisVersion + "]");
			}

			//Retrieve the value behind the node that matched the key/version
			if ((node != null))
			{
				obj = node.InnerText;
			}

		  //Check the value of the obj variable
			if (IsNumeric((string)obj))
			{
				//since its numeric convert it to an integer
				obj = Convert.ChangeType(obj, typeof(int));   
			}
			else
			{
				//otherwise convert it to a string
				obj = Convert.ChangeType(obj, typeof(string)); //(string)obj;
			}

		}

		return obj;
	}
#endregion




In the previous method there was a call to a method named PopulateCache. This is a fairly simple method, it attempts to load our XML configuration file using the Load Method of the XMLDocument Class. From there it inserts the entire document's contents into the cache for later retrieval:


#region PopulateCache
	/// <summary>
	/// Method for loading the configuration XML document
	/// into a cache object
	/// </summary>
	private void PopulateCache()
	{
		//With a try around the entire event, the object attempts to load the XML
		//file and store it in the cache with a dependency on the XML file itself.
		//This means that any time the XML file changes, it is removed from the 
		//cache.  When the "getConstant" method is called again, the XML file won't
		//exist in memory and the PopulateCache will be re-called.
		try
		{
			xmlDoc.Load(constantsFileName);
			constantsCache.Insert(constantsFileName, xmlDoc, new CacheDependency(constantsFileName));
		}
		catch (Exception e)
		{
			System.Diagnostics.Debug.WriteLine("Error: " + e.Message);
		}
	}
#endregion




Whenever we instantiate our object, we want to remove the version, in the event that the current Web Application may use a different version, so before we set the version number and hold it in the cache, we remove any remnants of the old version number. We do this by utilizing the Remove Method of the Cache Class:


#region ClearCache
	/// <summary>
	/// method for removing any remnants of the old
	///version number before we set the version number for
	/// the current Web Application
	/// </summary>
	public static void ClearCache()
	{
		//remove the old version number from
		//the current cacne object
		constantsCache.Remove(thisVersion);
	}
#endregion



You may have noticed a call to a method named IsNumeric in one of the previous methods. Since this is written in C#, and I didn't want to have to rely on a Visual Basic Namespace and function, I ended up creating my own IsNumeric Method so I can check or a numeric value:


#region IsNumeric
	/// <summary>
	/// method to check and see if the number 
	/// provided is a valid numeric value
	/// </summary>
	/// <param name="s">value to check</param>
	/// <returns></returns>
	private bool IsNumeric(string s)
	{
		try
		{
			//try to convert the value provided to
			//an integer and see what happens
			Convert.ToInt32(s);
			//conversion was successful
			return true;
		}
		catch (FormatException)
		{
			//not an integer
			return false;
		}
	}
#endregion




NOTE: In order to secure your XML constants file, rename the extension from XML to config. With ASP.NET, when files with the .config extension are requested, a This type of page is not served error message is returned, thereby protecting the constants from prying eyes!

I am providing the class file and project I created for this functionality. It is under the GNU - General Public License so you can modify, distribute or use in any way you please, but the lice file and headers must stay in place. I hope you found this tutorial useful and informative, Thank you for reading :)

Happy Coding!
Attached File  PC_ConstantsManager.zip (31.47K)
Number of downloads: 1323

Is This A Good Question/Topic? 0
  • +

Replies To: Using XML To Share Constants Across Multiple Web Applications in C#

#2 Nazi370   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 10
  • Joined: 10-November 08

Posted 13 March 2009 - 12:14 AM

thanks! ...

but it will be nice if there are some photo/images to show what actually had happen.. (outcome)

:D
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1