Click here to Skip to main content
15,895,011 members
Articles / Programming Languages / C#

Data Access and Transaction Handling Framework

Rate me:
Please Sign up or sign in to vote.
4.69/5 (107 votes)
13 Jan 200510 min read 927.9K   2.1K   282  
Generic data access component for different datasources, sprocs/SQL, implicitly propagated transactions, explicitly managed transaction contexts etc.
using System;
using System.Xml;
using System.IO;
using System.Reflection;
using System.Collections;
using System.Globalization;
using System.Configuration;

namespace Framework.Configuration
{
	public delegate void LoadXMLHandler(string xml);
	/// <summary>
	/// ConfigManager maintains the configuration data read from xml files.
	/// Setups watchers to handle xml files' changes.
	/// Supplies xml to the subscribed parties, which parse it and instantiate their objects.
	/// </summary>
	public class ConfigManager
	{
		private class ConfigEntry 
		{
			private Hashtable _xmlDocs = new Hashtable();
			private string _directoryPath = "";
			private string _fileMask = "";
			private Hashtable _elementHandlers = new Hashtable();

			public string DirectoryPath
			{
				get { return _directoryPath; }
				set { _directoryPath = value; }
			}
			public string FileMask
			{
				get { return _fileMask; }
				set { _fileMask = value; }
			}
			public Hashtable ElementHandlers			
			{
				get { return _elementHandlers; }
				set { _elementHandlers = value; }
			}
			//private static DateTime _configLastChanged = DateTime.Now;

			public void ExecuteHandler(string rootElement, LoadXMLHandler xmlHandler) 
			{
				XmlNode xmlNode = null;

				if(_xmlDocs.Count == 0) return;

				XmlDocument mergedDoc = new XmlDocument();
				xmlNode = mergedDoc.CreateElement("merged" + rootElement);
				mergedDoc.AppendChild(xmlNode);

				foreach(DictionaryEntry entry in _xmlDocs) 
				{
					xmlNode = ((XmlDocument)entry.Value).SelectSingleNode("//" + rootElement);
					xmlNode = mergedDoc.ImportNode(xmlNode, true);
					mergedDoc.DocumentElement.AppendChild(xmlNode);
				}

				if(mergedDoc.ChildNodes[0].ChildNodes.Count > 0)
					xmlHandler(mergedDoc.OuterXml);
			}

			public void ExecuteHandlers() 
			{
				foreach(DictionaryEntry entry in _elementHandlers) 
				{
					ExecuteHandler((string)entry.Key, (LoadXMLHandler)entry.Value);
				}
			}

			public void LoadXmlDocumentFromFiles() 
			{
				string[] fileNames = Directory.GetFiles(_directoryPath, _fileMask);

				if(fileNames.Length == 0) 
					throw new ConfigurationException("No config files found with mask " + _directoryPath + Path.DirectorySeparatorChar + _fileMask + ".");

				foreach(string fileName in fileNames) 
				{
					LoadXmlDocumentFromFile(fileName);
				}
			}

			private void LoadXmlDocumentFromFile(string fileName) 
			{
				fileName = fileName.ToLower(CultureInfo.InvariantCulture);

				XmlDocument doc = new XmlDocument();
				XmlTextReader reader = new XmlTextReader(fileName);
				reader.WhitespaceHandling = WhitespaceHandling.None;
				doc.Load(reader);
				reader.Close();
				if(_xmlDocs.Contains(fileName))
					_xmlDocs[fileName] = doc;
				else
					_xmlDocs.Add(fileName, doc);
			}

			public void SetupWatcher() 
			{
				FileSystemWatcher watcher = new FileSystemWatcher(DirectoryPath, FileMask);
				watcher.NotifyFilter = NotifyFilters.LastWrite;
				watcher.Changed += new FileSystemEventHandler(this.Config_Changed);
				watcher.EnableRaisingEvents = true;
				//_watchers.Add(...
			}

			private void Config_Changed(object sender, FileSystemEventArgs e) 
			{
/*
				//this is a hack to prevent the event from being double-raised
				TimeSpan timeDif = DateTime.Now - _configLastChanged;
				if(timeDif.Seconds > 2) 
				{
					//LoadDataSources(e.FullPath);
					_configLastChanged = DateTime.Now;
				}
*/
				//LoadXmlDocumentFromFile(e.FullPath);
				LoadXmlDocumentFromFile(e.FullPath);
				ExecuteHandlers();
			}
		}

		private static Hashtable _configEntries = new Hashtable();

		static ConfigManager() 
		{

		}

		private ConfigManager() {}

		public static void ProcessConfig(string rootElement, LoadXMLHandler xmlHandler) 
		{
			//gets the calling assembly directory and name
			//duplication needed because of Assembly.GetCallingAssembly
			string codeBase = Assembly.GetCallingAssembly().CodeBase;
			string directoryPath = Path.GetDirectoryName(codeBase.Replace(@"file:///",""));
			string fileMask = Path.GetFileName(codeBase).Replace(".DLL", ".dll.config");

			ProcessConfig(directoryPath, fileMask, rootElement, xmlHandler);
		}

		public static void ProcessConfig(string directoryPath, string fileMask, 
			string rootElement, LoadXMLHandler xmlHandler) 
		{
			if(directoryPath == null || directoryPath == string.Empty 
				|| fileMask == null || fileMask == string.Empty)
			{
				//gets the calling assembly directory and name
				//duplication needed because of Assembly.GetCallingAssembly
				string codeBase = Assembly.GetCallingAssembly().CodeBase;

				if(directoryPath == null || directoryPath == string.Empty) 
				{
					directoryPath = Path.GetDirectoryName(codeBase.Replace(@"file:///",""));
				}

				if(fileMask == null || fileMask == string.Empty)
				{
					fileMask = Path.GetFileName(codeBase).Replace(".DLL", ".dll.config");
				}
			}

			string fullPath = directoryPath + Path.DirectorySeparatorChar + fileMask;

			//try to get config path from app.config
			string overrideFullPath = ConfigurationSettings.AppSettings[rootElement];
			if(overrideFullPath != null) 
			{
				fullPath = overrideFullPath;
				directoryPath = Path.GetDirectoryName(fullPath);
				fileMask = Path.GetFileName(fullPath);
			}

			ConfigEntry configEntry = _configEntries[fullPath] as ConfigEntry;
			if(configEntry == null) 
			{
				configEntry = new ConfigEntry();
				_configEntries.Add(fullPath, configEntry);

				configEntry.DirectoryPath = directoryPath;
				configEntry.FileMask = fileMask;
				configEntry.LoadXmlDocumentFromFiles();
				configEntry.SetupWatcher();
			}

			if(!configEntry.ElementHandlers.Contains(rootElement))
				configEntry.ElementHandlers.Add(rootElement, xmlHandler);

			configEntry.ExecuteHandler(rootElement, xmlHandler);
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Austria Austria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions