Basic Assembly Configuration

Sunday, November 14, 2004 12:00:00 AM (Central Standard Time, UTC-06:00)
Here is simple implementation that supports a configuration file for an assembly.  I say simple because it only takes in account the configuration for the dll, it does not include the app, machine or other configs that make up the configuration of an application.
 
The AssemblyConfiguration class by default requires the dll that contains the AssemblyConfiguration and the dll that you want to support configuration are in the same directory.  You can override the behavior by using the constructor that takes the path to the assembly and a reference to the assembly. 
 
    1 using System;
    2 using System.Configuration;
    3 using System.Reflection;
    4 using System.Xml;
    5 using System.Windows.Forms;
    6  
    7  
    8 using TidByte.Activation;
    9  
   10  
   11 namespace TidByte.Configuration
   12 {
   13     /// <summary>
   14     /// Supports the loading of a configuration file for a .dll
   15     /// </summary>
   16     public class AssemblyConfiguration
   17     {
   18         string        _cfgFile = string.Empty;
   19         XmlDocument    _doc = null;
   20         string        _appPath = string.Empty;
   21  
   22         /// <summary>
   23         /// Default constructor that assumes that the the TidByte.Configuration dll is located in the same directory as the
   24         /// .dll that requires the AssemblyConfiguration implementation.  If you need to override this behavior, you will need to
   25         /// use the constructor that supports the identification of a path of where the dll that required configuration is located.
   26         /// </summary>
   27         public AssemblyConfiguration() : this (AppDomain.CurrentDomain.BaseDirectory, Assembly.GetCallingAssembly()) {    }
   28  
   29         /// <summary>
   30         /// Supports identifying the path of the assembly that requires configuration.  Defaults to the calling assembly as the assembly
   31         /// that requires configuration.
   32         /// </summary>
   33         /// <param name="appPath">Path to assembly to requires identification</param>
   34         public AssemblyConfiguration(string appPath) : this (appPath, Assembly.GetCallingAssembly()) {    }
   35  
   36         /// <summary>
   37         /// Supply the path, and assembly that requires configuration.
   38         /// </summary>
   39         /// <param name="appPath"></param>
   40         /// <param name="asm"></param>
   41         public AssemblyConfiguration(string appPath, Assembly asm)
   42         {
   43             //set the dll config file name
   44             _cfgFile = asm.CodeBase + ".config";
   45  
   46             //load it into the DOM
   47             _doc = new XmlDocument();
   48             _doc.Load(new XmlTextReader(_cfgFile));
   49  
   50             _appPath = appPath;
   51  
   52         }
   53  
   54  
   55         /// <summary>
   56         /// Returns the configuration according to the configSection defined in the config file
   57         /// </summary>
   58         /// <param name="section">Section of the config file that supports the configuration settings.</param>
   59         /// <returns></returns>
   60         public object GetConfig(string section)
   61         {
   62             //gets the config section that is requested
   63             XmlNode sectionNode = _doc["configuration"]["configSections"].SelectSingleNode("./section[@name='" + section + "']");
   64  
   65             if (sectionNode != null)
   66             {
   67                 //get the type of the object to create
   68                 string typeDef = sectionNode.Attributes["type"].Value;
   69                 IConfigurationSectionHandler cfgHandler = (IConfigurationSectionHandler)ActivationHelper.ActivateFromPath(typeDef, _appPath);
   70  
   71                 XmlNode sectionXml = _doc["configuration"].SelectSingleNode("./" + section);
   72  
   73                 //Call the interface
   74                 return cfgHandler.Create(sectionNode.ParentNode,null,sectionXml);
   75  
   76             }
   77  
   78             return null;
   79         }
   80  
   81     }
   82 }
 
The next code snippet shows the implementation for how the ActivationHelper works:
    1 using System;
    2 using System.Reflection;
    3 using System.Windows.Forms;
    4  
    5 namespace TidByte.Activation
    6 {
    7     /// <summary>
    8     /// Summary description for ActivationHelper.
    9     /// </summary>
   10     public class ActivationHelper
   11     {
   12         const int AssemblyName    = 1;
   13         const int ClassName        = 0;
   14  
   15         //default constructor
   16         public ActivationHelper() {}
   17  
   18         /// <summary>
   19         /// Private helper function to assist in run-time activations. Returns
   20         /// an object from the specified assembly and type.
   21         /// </summary>
   22         /// <param name="assembly">Name of the assembly file (w/out extension)</param>
   23         /// <param name="typeName">Name of the type to create (namespace must be included)</param>
   24         /// <returns>Instance of the type specified in the input parameters.</returns>
   25         public static object Activate(string assembly, string typeName)
   26         {
   27             try
   28             {
   29            
   30                 return AppDomain.CurrentDomain.CreateInstanceAndUnwrap(assembly, typeName);
   31             }
   32             catch( Exception ex)
   33             {
   34                 throw ex;
   35             }
   36         }
   37  
   38         /// <summary>
   39         /// Private helper function to assist in run-time activations. Returns
   40         /// an object from the specified assembly and type, as defined in a standard config
   41         /// </summary>
   42         /// <param name="arArgs"> namespace.class (comma seperated),assembly filename</param>
   43         /// <returns>Instance of the type specified in the input parameters.</returns>
   44         public static object Activate(string assm)
   45         {
   46            
   47             string[] arArgs = assm.Split(new char[]{','});
   48             string assmname = arArgs[AssemblyName].Trim();
   49             string clsname = arArgs[ClassName].Trim();
   50                    
   51  
   52             //Dynamically load the deployment handler from the config file
   53             return Activate(assmname, clsname);
   54  
   55         }
   56  
   57         /// <summary>
   58         /// Activate a specific assembly from a specified path
   59         /// </summary>
   60         /// <param name="assm"></param>
   61         /// <param name="path"></param>
   62         /// <returns></returns>
   63         public static object ActivateFromPath(string assm, string path)
   64         {
   65  
   66            
   67             string[] arArgs = assm.Split(new char[]{','});
   68             string assmname = arArgs[AssemblyName].Trim();
   69             string clsname = arArgs[ClassName].Trim();
   70  
   71             Assembly lib = Assembly.LoadFrom(path + assmname + ".dll");
   72  
   73             return lib.CreateInstance(clsname);
   74         }
   75     }
   76 }
 
And last I am using a modified version of Craig Andreas "The last configuration handler I'll ever need". 
 
    1 using System;
    2 using System.Reflection;
    3 using System.IO;
    4 using System.Configuration;
    5 using System.Xml;
    6 using System.Xml.Serialization;
    7 using System.Xml.XPath;
    8 using System.Windows.Forms;
    9  
   10  
   11 using TidByte.Activation;
   12  
   13 namespace TidByte.Configuration
   14 {
   15  
   16     /*
   17     * When you map a section of your configuration file to this handler, using an entry in your config file that looks like this:
   18  
   19         <configuration>
   20             <configSections>
   21                 <section name="MyStuff" type="<Namespace>.XmlSerializerSectionHandler, Exe/DllName"/>
   22             </configSections>
   23         </configuration>
   24        
   25         What happens is that any time you ask for the MyStuff section of the config file with code that looks like this:
   26         AppConfig.GetTypeSafeConfig("MyStuff");
   27  
   28         It will actually go look in the config file to figure out who knows how to deal with the “MyStuff” section.
   29         When it sees that it’s the XmlSerializerSectionHandler, it’ll create an instance of this object and call Create,
   30         passing it (via the section parameter) a reference to the relevant portion of the config file. In our case, it might look something like this:
   31             <configuration>
   32                 <!-- configSections element goes here -->
   33                 <MyStuff type="SomeNamespace.MyStuff, CraigsConfig">
   34                     <Foo>1.234</Foo>
   35                     <Bar>A bunch of information</Bar>
   36                 </MyStuff>
   37             </configuration>
   38  
   39         What my handler does is to look for the “type” attribute on this bit of XML, in the form “classname, assemblyname”.
   40         In this case, it’s a class called MyStuff in a namespace SomeNamespace. It then – and here’s the good bit –
   41         uses the XmlSerializer to initialize an instance of this type from the XML in the configuration file.
   42         The XmlSerializer is a very cool piece of the .NET libraries, and you should check it out if you haven’t already.
   43         Basically, you write a type like this:
   44  
   45             public class MyStuff
   46             {
   47                 private float foo;
   48                 private string bar;
   49  
   50                 public float Foo
   51                 {
   52                     get { return foo; }
   53                     set { foo = value; }
   54                 }
   55  
   56                 public string Bar
   57                 {
   58                     get { return bar; }
   59                     set { bar = value;
   60                 }
   61             }
   62  
   63         And the XmlSerializer takes care of turning it into the XML above and vice versa. There are even a bunch of
   64         attributes you can use to control the process.
   65        
   66         [XmlRoot("RootElemenet")]                - allows the naming of a root element
   67         [XmlElement("FileName")]                - overrides a property name
   68         [XmlAttribute("FileName")]                - stores a property as an attribute
   69         [XmlArray("CollectionName")]            - assigns a collection a name
   70         [XmlArrayItem("CollectionChildName")]    - assigns the children of the collection a name
   71  
   72         Calling:
   73  
   74         MyStuff ms = (MyStuff) AppConfigEx.GetTypeSafeConfig("MyStuff");
   75     */
   76  
   77     public class XmlSerializerSectionHandler :    IConfigurationSectionHandler
   78     {
   79         public object Create(
   80             object parent,
   81             object configContext,
   82             System.Xml.XmlNode section)
   83         {
   84             try
   85             {
   86                 //get the type that we need to create to support the configuration
   87                 XPathNavigator nav = section.CreateNavigator();
   88                 string typename = (string) nav.Evaluate("string(@type)");
   89  
   90                 //Get the location of this dll. We assume that all dll's loaded through the config are installed to a private
   91                 FileInfo fi = new FileInfo(Assembly.GetExecutingAssembly().Location);
   92                 Type t = ActivationHelper.ActivateFromPath(typename, fi.DirectoryName + @"\").GetType();
   93                 XmlSerializer ser = new XmlSerializer(t);
   94  
   95  
   96                 //xpath will select all nodes below the parent and use those to create the type;
   97                 return ser.Deserialize(new XmlNodeReader(section.SelectSingleNode("*")));
   98                
   99             }
  100             catch (Exception ex)
  101             {
  102                 Console.WriteLine(ex.ToString());
  103                 throw ex;
  104             }
  105         }
  106  
  107     }
  108  
  109 }
Comments are closed.