in

ASP.NET Weblogs

The .NET Buffet

.Net, .Net CF, Architecture, and more (would you like Fries with that?)
  • VBCommenter Source

    OK, so it's been a really long time since I've posted here.  In any case, I noticed that in the very recent past several people were looking for the source to VBCommenter.  As it stands, the GotDotNet site is toast, but if you look hard enough, you can still find downloads on their site for VBCommenter.

    I've downloaded the 1.2.6 source code and posted it here so others can continue working on it.  I'd recommend posting the code to CodePlex if there's really still a strong need for this.  Given VS.NET 2005 and 2008 both support XML comments in VB.NET code, I had long ago dropped this project.  I'm hopefully that posting the code will help out those who are still maintaining .Net Framework 1.0/1.1 applications in VB.NET.  Go forth and code!

    Doug

  • VBCommenter 1.2.5 FINAL released

    Anyone who is using VB.NET and wished for support for XML comment generation (NDoc, anyone?) should be using VBCommenter.  As I found myself in that very situation over the last few months, I've made some fairly major improvements to the VBCommenter add-in, and have wrapped up a new release.

    After struggling with IE trying to convince it to load hosted .Net controls using the 1.1 framework (I have the VS.NET 2005 beta installed and, by default, IE will use the latest runtime for hosted controls), I've managed to get the 1.2.5 VBCommenter release completed, in source control, and available.  Head to the VBCommenter Workspace Home for the download.  Here are the highlights between 1.2 and 1.2.5:

    - Major speed improvements (mostly related to schema and XML validation handling). For example, a large project that took 14 minutes to build using 1.2 and 2 minutes without VBCommenter takes about 2.5 minutes with 1.2.5.
    - Improved comment schema (includes all NDoc extensions to the XML comment schema)
    - Improved XML validation and task list reporting
    - Fixed defect relating to Reporting Services projects (MSDN article ID 842434) - your Intellisense will no longer go away when you build a solution with a Reporting Project in it.
    - Improved interoperability with other add-ins by not forcing the IDE to shadow copy all assemblies it loaded in the add-in appdomain. This is known to resolve issues with BizTalk 2004, Borland's Together Edition for Microsoft Visual Studio .Net, and (potentially) AnkhSVN.
    - <see cref=""/> will output the correct XML for the cref attribute.
    - The XmlWriter.FindType method has been simplified and its speed improved dramatically, increasing the reliability and performance of the add-in.
    - cref resolution is much better - If your class has a method called "Log" and you want to reference it, the generated cref will be correct instead of pointing to System.Diagnostics.Log
    - Handles Enterprise Template Projects correctly
    - Reads assemblies in read-only mode to avoid locking the file or the inability to load the assembly if the IDE has it locked
    - Fixes issues with duplicate comment prefixes being created
    - Renders variables declared as "WithEvents" as properties (which is how the WithEvents keyword is implemented in IL) to allow NDoc to find the appropriate comments for the field.

  • End to End EntLib Demonstration (Source and Powerpoint presentation)

    Ok, so between naps (of my 21-month-old son) and some late nights, I've managed to get the source code from my presentation last Tuesday put together.  You can downoad the source and my presentation and follow the readme.txt to get set up.  The source has examples of consuming all of the application blocks.  It also includes an example of how to integrate ASP.NET's Forms Authentication and EntLib's Authentication/Authorization block.

    The example application presented and the presentation itself were both based on (ok, mostly copied from) the earlier work of some great people at Avanade - Jeff Donahoe, Kyle Burkholder, and Ben Reierson.  I'd like to thank them for their efforts to put this together - hopefully some of my additions will be useful to them as well.

    Over the next few weeks, naps permitting, I'll be writing up a blog entry for each block and using this source for my explainations.  But not tonight - too much time in front of the computer and not enough time spent with the family over the last week.

    Feel free to post here if you have any issues or questions.  I'll try my best to help out.

    Doug

  • Another VB.NET note (The BuildRules add-in)

    On another VB.NET note, for anyone using the BuildRules tool included in the Microsoft Visual Studio .Net 2003 Automation Samples and have noticed that when you have a reporting project in your solution the BuildRules tree doesn't populate itself, the bug is essentially the same bug as the one I fixed in VBCommenter.  There's a "foreach (project in solution.projects)" or some such loop in the BuildRules code, which fails somewhat silently when a reporting services project is in the solution.  If you change it to an indexed loop going from 1 to the count of the Solution.Projects.Count property (yes, it really is a one-based array) and get the project using the Projects.Item(index) property, it will work again.  This appears to have something to do with the COM interface to VS.NET and its implementation of the COM equivalent of IEnumerable.GetEnumerator (NewEnum? Sorry, I wasn't a COM guy before .NET) method, which fails in this case.

    Specifics (DISCLAIMER: this is mostly untested code, but should work):

    On line 266 of the RulesFrm.vb file, you'll find this:

      266         For Each project In applicationObject.Solution.Projects

      267             Dim globals As EnvDTE.Globals

      268             Try

      269                 globals = project.Globals

      270             Catch

      271                 globals = Nothing

      272             End Try

      273 

      274             If Not (globals Is Nothing) Then

      275                 projectNode = preBuildNode.Nodes.Add(project.Name)

      276                 projectNode.Tag = project

      277                 projectNode = postBuildNode.Nodes.Add(project.Name)

      278                 projectNode.Tag = project

      279             End If

      280         Next

    If you change it to read:

      266         For i As Integer = 1 To applicationObject.Solution.Projects.Count

      267             project = applicationObject.Solution.Projects.Item(i)

      268             Dim globals As EnvDTE.Globals

      269             Try

      270                 globals = project.Globals

      271             Catch

      272                 globals = Nothing

      273             End Try

      274 

      275             If Not (globals Is Nothing) Then

      276                 projectNode = preBuildNode.Nodes.Add(project.Name)

      277                 projectNode.Tag = project

      278                 projectNode = postBuildNode.Nodes.Add(project.Name)

      279                 projectNode.Tag = project

      280             End If

      281 

      282         Next

    It should fix the issue and allow you to use the BuildRules add-in with Reporting Services projects.  It seems strange, but it's true!

  • A new VBCommenter release for those of you using VB.NET...

    I've just posted a new release of VBCommenter on the GotDotNet workspaces.  This tool has been invaluable to us on my current project, where VB.NET has been mandated but where we wanted to create our API documentation just like a C# project.  However, there were several issues with the 1.2 release that caused us issues, and over the last few months I've been slowly working on cleaning them up.  Major improvements over the 1.2 version include:

    • Improved performance - Build times on our (admitedly highly-commented) solution have gone from 14 minutes to 2 minutes with XML comment generation turned on (and about 1.5 minutes without)
    • Improved error handling and reporting - I've included the C# Comment Schema from Daniel Cazzulino's Blog, with a few minor modifications to make it validate fragments of XML comments.  This schema is much more accurate than the original included with VBCommenter, and also helped improve performance because there are fewer XML comment errors thrown by the add-in.
    • Fixed a bug related to Reporting Services projects - see this Microsoft support article for more details on the problem. 

    There are still a few things I'd like to improve, so I've labeled this a "release candidate," but we're using it heavily on my current project and others at my client and things seem to work well so far.

    Check out the VBCommenter Workspace Home for more information and the download.

    And, for those of you awaiting the source code from my presentation at the Cincinnati .NET User's Group, it will be up tomorrow as promised.  I'll be posting on all of the major EntLib blocks in the next few weeks, using this solution as a running example.

  • My EntLib presentation to the Cincinnati .Net users' group meeting is TONIGHT (2/15) at 6

    If you happen to live in the Cincinnati area and read my blog - I'm giving a presentation on Enterprise Library tonight at the Cincinnati .Net Users Group - see their site for directions.  The presentation will start at 6pm and last until around 7.

    I hope to see you there!

    Doug

  • Enterprise Library Configuration (Part 2/2): Customizable configuration with framework extensions.

    In my first article on Enterprise Library configuration, I talked about how to build your own "root node" in a configuration file.  We also discussed how to use the ILinkedNodeService to reference another configuration node from within your custom configuration section, and went over some details as to how the code worked in relation to the configuration manager tool.  I provided a downloadable code example that I will use again in this post.

    Before I get started, I wanted to point you to the tool that myself (and many others) use to get our VS.NET code into such nice HTML format - it's called CopySourceAsHtml by Colin Coller.  Thanks for such a time-saving tool.

    In this post, I'm going to discuss how you can create customized "providers" (the Enterprise Library term for pluggable modules that fit within the code EntLib frameworks), and how those providers can use the configuration tool to gather customizable information from the user beyond default key/value pairs you get without any additional work.  We're going to build an Authorization Provider that will authorize a given request based solely on the system's pseudo-random number generator and a threshold value provided through the configuration framework.

    If you open up the solution, we'll be looking at the EntLibFrameworkConfigDemo project this time.  I want to start with the MyAuthenticationProvider class, which is the actual random authenticator.

    MyAuthorizationProvider.cs

     

        1 using System;

        2 using Microsoft.Practices.EnterpriseLibrary.Configuration;

        3 using Microsoft.Practices.EnterpriseLibrary.Security;

        4 

        5 

        6 namespace EntLibConfigDemo.ExtendingFrameworks

        7 {

        8     /// <summary>

        9     /// Summary description for MyAuthenticationProvider.

       10     /// </summary>

       11     public class MyAuthorizationProvider: ConfigurationProvider, IAuthorizationProvider

       12     {

       13 

       14         private static System.Random _rnd = new System.Random();

       15 

       16         private double threshold;

       17         private string applicationName;

       18 

       19         public MyAuthorizationProvider()

       20         {

       21             // No constructor logic needed

       22         }

       23         #region IAuthorizationProvider Members

       24 

       25         public bool Authorize(System.Security.Principal.IPrincipal principal, string context)

       26         {

       27             if (_rnd.NextDouble()>threshold)

       28             {

       29                 return true;

       30             }

       31             return false;

       32         }

       33 

       34         #endregion

       35 

       36         #region IConfigurationProvider Members

       37 

       38         private SecurityConfigurationView securityConfigurationView;

       39 

       40         /// <summary>

       41         /// Initializes the state of the current object from

       42         /// the specified configuration data.

       43         /// </summary>

       44         /// <param name="configurationView">A <see cref="SecurityConfigurationView"></see> object</param>

       45         public override void Initialize(ConfigurationView configurationView)

       46         {

       47             // Check to make sure we have a valid configuration node before continuing

       48             if (null==configurationView)

       49             {

       50                 throw new ArgumentNullException("configurationView cannot be null");

       51             }

       52             securityConfigurationView = configurationView as SecurityConfigurationView;

       53             if (null==securityConfigurationView)

       54             {

       55                 throw new ArgumentException("configurationView is not of the correct type.");

       56             }

       57 

       58             // Load our authorization data and store the relevant information for later use

       59             MyAuthorizationProviderData data = securityConfigurationView.GetAuthorizationProviderData(ConfigurationName) as MyAuthorizationProviderData;

       60             if (null==data) {

       61                 throw new ArgumentException("AuthorizationProviderData is not of type MyAuthorizationProviderData");

       62             }

       63             threshold = data.AuthorizationRate;

       64             applicationName = data.Application;

       65         }

       66         #endregion

       67     }

       68 }

    The IConfigurationManager interface:

    What we're interested in here is the IConfigurationManager implementation, which is nothing more than the Initialize method.  This method is called when the provider is first loaded, and provides the framework a way to pass configuration data to the provider.  Here we check to make sure we've received the right type of configuration data and then initialize the private variables from the configuration data.  Pretty simple all in all.  The SecurityConfigurationView class is part of the core EntLib class library and provides several utility methods including the GetAuthorizationProviderData we use to retrieve the authorization data, which is defined in the MyAuthroizationProviderData class.  Both the MyAuthorizationData and the MyAuthroizationProviderNode don't add anything new from my previous post, so I'm not going to go into detail on them here.  The major difference between the two examples is in the DesignManager class, in this case MyAuthorizationProviderDesignManager class:

    MyAuthorizationProviderDesignManager.cs

     

        1 //===============================================================================

        2 // Microsoft patterns & practices Enterprise Library

        3 // Security Application Block

        4 //===============================================================================

        5 // Copyright © Microsoft Corporation.  All rights reserved.

        6 // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY

        7 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT

        8 // LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND

        9 // FITNESS FOR A PARTICULAR PURPOSE.

       10 //===============================================================================

       11 

       12 using System;

       13 using System.Diagnostics;

       14 using Microsoft.Practices.EnterpriseLibrary.Configuration;

       15 using Microsoft.Practices.EnterpriseLibrary.Configuration.Design;

       16 using Microsoft.Practices.EnterpriseLibrary.Security.Configuration;

       17 

       18 namespace EntLibConfigDemo.ExtendingFrameworks

       19 {

       20     /// <summary>

       21     /// Configuration Design Manager for MyAuthorizationProvider. <seealso cref="IConfigurationDesignManager"/>.

       22     /// </summary>

       23     public class MyConfigurationDesignManager : IConfigurationDesignManager

       24     {

       25         /// <summary>

       26         /// Initializes a new instance of the <see cref="MyConfigurationDesignManager"/> class.

       27         /// </summary>

       28         public MyConfigurationDesignManager()

       29         {

       30         }

       31 

       32         /// <summary>

       33         /// <para>Registers the <see cref="MyAuthorizationProviderNode"/> in the application.</para>

       34         /// </summary>

       35         /// <param name="serviceProvider">

       36         /// <para>The a mechanism for retrieving a service object; that is, an object that provides custom support to other objects.</para>

       37         /// </param>

       38         public void Register(IServiceProvider serviceProvider)

       39         {

       40             RegisterNodeTypes(serviceProvider);

       41             RegisterXmlIncludeTypes(serviceProvider);

       42         }

       43 

       44         /// <summary>

       45         /// <para>Opens the configuration settings and registers them with the application.</para>

       46         /// </summary>

       47         /// <param name="serviceProvider">

       48         /// <para>The a mechanism for retrieving a service object; that is, an object that provides custom support to other objects.</para>

       49         /// </param>

       50         public void Open(IServiceProvider serviceProvider)

       51         {

       52         }

       53 

       54         /// <summary>

       55         /// <para>Saves the configuration settings created for the application.</para>

       56         /// </summary>

       57         /// <param name="serviceProvider">

       58         /// <para>The a mechanism for retrieving a service object; that is, an object that provides custom support to other objects.</para>

       59         /// </param>

       60         public void Save(IServiceProvider serviceProvider)

       61         {

       62         }

       63 

       64         /// <summary>

       65         /// <para>Adds to the dictionary configuration data for

       66         /// the enterpriselibrary.configurationSettings configuration section.</para>

       67         /// </summary>

       68         /// <param name="serviceProvider">

       69         /// <para>The a mechanism for retrieving a service object; that is, an object that provides custom support to other objects.</para>

       70         /// </param>

       71         /// <param name="configurationDictionary">

       72         /// <para>A <see cref="ConfigurationDictionary"/> to add

       73         /// configuration data to.</para></param>

       74         public void BuildContext(IServiceProvider serviceProvider, ConfigurationDictionary configurationDictionary)

       75         {

       76         }

       77 

       78         private static void RegisterXmlIncludeTypes(IServiceProvider serviceProvider)

       79         {

       80             // Find the xmlIncludeTypeService, which registers the XML-serializable class representing the configuration data.

       81             IXmlIncludeTypeService xmlIncludeTypeService = serviceProvider.GetService(typeof(IXmlIncludeTypeService)) as IXmlIncludeTypeService;

       82             Debug.Assert(xmlIncludeTypeService != null, "Could not find the IXmlIncludeTypeService");

       83             // Register the MyAuthorizationProviderData type with the xmlIncludeTypeService

       84             xmlIncludeTypeService.AddXmlIncludeType(SecuritySettings.SectionName, typeof(MyAuthorizationProviderData));

       85         }

       86 

       87         private static void RegisterNodeTypes(IServiceProvider serviceProvider)

       88         {

       89             // Get the Node Creation Service to we can register our configuration node (the designer class) with the configuration tool.

       90             INodeCreationService nodeCreationService = ServiceHelper.GetNodeCreationService(serviceProvider);

       91 

       92             Type nodeType = typeof(MyAuthorizationProviderNode);

       93             // Register a new command with the Node Creation Service, allowing multiple instances within a single configuration file.

       94             // The DisplayName parameter is what is displayed in the configuration manager menu.

       95             NodeCreationEntry entry = NodeCreationEntry.CreateNodeCreationEntryWithMultiples(new AddChildNodeCommand(serviceProvider, nodeType), nodeType, typeof(MyAuthorizationProviderData), "My Authorization Provider");

       96             nodeCreationService.AddNodeCreationEntry(entry);

       97         }

       98     }

       99 }

    Because we are simply adding a new provider of a type the configuration manager already knows something about, we just have to figure out how to add our type to the correct menu item.  In this case, the key is to know that in the RegisterXmlIncludeTypes method to call the AddXmlIncludeType takes the section name as its first parameter.  Otherwise, the major difference between this class and the AppConfig example is that this one is much simpler.  Most of the implementation of the IConfigurationDesignManager interface is not necessary, and there is no CreateCommands method, as the command to create an authentication provider node is already part of the configuration manager.

    With that, I'll leave you to explore the Enterprise Library configuration management classes.

  • Enterprise Library Configuration (Part 1/2): Customized Appsettings and the EntLib Configuration Manager tool.

    So I've finally gotten comfortable enough with the configuration management tool to post some examples.  I'll be doing a 2-part series on the Enterprise Library configuration framework and how you can incorporate your own application configuration into the tool and also customize the configuration offered by your custom frameworks. 

    I'd also like to point out the posts of Hisham Baz and Scott Densmore who have done some similar work and has a few other walk-throughs on both the Configuration topic and others.

    I've included two projects in the downloadable solution.  The first is essentially a customized appsettings node that can be edited in the configuration tool.  The second is an overly simplified Authorization manager, which shows how to extend the existing frameworks and provide a user-friendly way to edit the configuration data for those extensions.

    Update:  I missed four lines of code in my example download code (sent an older version to James) and as of 11:51 PM EST on 2/2/2005, I've sent the update to James to get to his site.  If you download it before he uploads the update, simply take the code in the blog post for the AppConfigNode.cs file and replace the code in the download with this code.  Sorry for the inconvenience.

    Update 2: It's now 9:45am on 2/3/2005 and James has updated the download.

    My good friend James was nice enough to host the example's source for me, so, grab the code, sit back, and let's take a look.

    Customized Application Configuration Nodes

    Each customized configuration node requires several classes in order to be completely integrated with the configuration management tool.  The first, as explained in the EntLib Quick Starts, is the container class, usually named with a postfix of "Data."  This class is the class that is actually serialized to the config file, and is fairly simple.

    AppConfigData.cs

      

      1 using System;

        2 using System.Xml.Serialization;

        3 using EntLib = Microsoft.Practices.EnterpriseLibrary.Configuration;

        4 

        5 namespace EntLibConfigDemo {

        6     /// <summary>

        7     /// This class is the serializable representation of our configuration data.

        8     /// </summary>

        9     ///

       10     [XmlRoot()]

       11     public class AppConfigData {

       12 

       13         public AppConfigData() {

       14         }

       15 

       16         public AppConfigData(string name) {

       17             this._name = name;

       18         }

       19 

       20         private string _url;

       21 

       22         /// <summary>

       23         /// The URL for the application

       24         /// </summary>

       25         [XmlElement("Url")]

       26         public string Url {

       27             get {

       28                 return _url;

       29             }

       30             set {

       31                 _url = value;

       32             }

       33         }

       34 

       35         private int _maxUsers;

       36 

       37         [XmlElement("MaxUsers")]

       38         public int MaxUsers {

       39             get {

       40                 return _maxUsers;

       41             }

       42             set {

       43                 _maxUsers = value;

       44             }

       45         }

       46 

       47         private string _database;

       48 

       49         [XmlElement("Database")]

       50         public string Database {

       51             get {

       52                 return _database;

       53             }

       54             set {

       55                 _database = value;

       56             }

       57         }

       58 

       59         private string _name;

       60 

       61         [XmlElement("Name")]

       62         public string Name {

       63             get {

       64                 return _name;

       65             }

       66             set {

       67                 _name = value;

       68             }

       69         }

       70 

       71         /// <summary>

       72         /// Used to demonstrate the configuration manager's ability to display enums easily

       73         /// </summary>

       74         public enum AppState {

       75             Unknown,

       76             Up,

       77             Down

       78         }

       79 

       80         private AppState _state;

       81 

       82         [XmlElement("State")]

       83         public AppState State {

       84             get {

       85                 return _state;

       86             }

       87             set {

       88                 _state = value;

       89             }

       90         }

       91     }

       92 }

    Note that the "State" property is defined by an Enum type, and in the configuration tool you will find that this data is represented by a drop-down list as you would expect.  As stated earlier, this class is really just a data container, and the next class, the AppConfigNode, defines how this class is displayed in the configuration tool and what happens when certain events like node renames or removes happen in the tool.  These events allow you to utilize other configuration nodes, like the database in this example, and be assured that when the user renames or removes them you can handle those changes appropriately.  Here's  the source for the AppConfigNode class, which we'll dig into more once you've read over it.

    AppConfigNode.cs

     

        1 using System;

        2 using System.ComponentModel;

        3 using System.Drawing.Design;

        4 

        5 using Microsoft.Practices.EnterpriseLibrary.Configuration.Design;

        6 using Microsoft.Practices.EnterpriseLibrary.Data.Configuration;

        7 using Microsoft.Practices.EnterpriseLibrary.Data.Configuration.Design;

        8 using Microsoft.Practices.EnterpriseLibrary.Configuration.Design.Validation;

        9 

       10 namespace EntLibConfigDemo {

       11     /// <summary>

       12     /// AppConfigNode is the configuration-time representation of the AppConfigData class.

       13     /// The ServiceDependency attribute informs the configuration manager that this class

       14     /// needs to instantialte an ILinkNodeService to make sure that other configuration nodes

       15     /// on which this node depends can be found.

       16     /// </summary>

       17     /// <remarks>

       18     /// Apply attributes from the System.ComponentModel namespace to tbe public properties to control their

       19     /// behavior in the configuration tool

       20     /// </remarks>

       21     [ServiceDependency(typeof(ILinkNodeService))]

       22     public class AppConfigNode: ConfigurationNode {

       23 

       24         #region Constructors

       25 

       26         public AppConfigNode():this(new AppConfigData("Application Configuration")) {

       27         }

       28 

       29         public AppConfigNode(AppConfigData data) {

       30             _appConfigData = data;

       31         }

       32 

       33         #endregion

       34 

       35         #region Public properties

       36 

       37         private AppConfigData _appConfigData;

       38 

       39         [Browsable(false)]

       40         public AppConfigData Data {

       41             get {

       42                 return _appConfigData;

       43             }

       44         }

       45 

       46         [Category("General"), Description("Url of the system"), Required()]

       47         public string Url {

       48             get {

       49                 return _appConfigData.Url;

       50             }

       51             set {

       52                 _appConfigData.Url = value;

       53             }

       54 

       55         }

       56 

       57         [Category("General"), ReadOnly(true)]

       58         public override string Name {

       59             get { return base.Name; }

       60             set { base.Name = value; }

       61         }

       62 

       63         [Category("General"), Description("Maximum Users Allowed")]

       64         public int MaxUsers {

       65             get {

       66                 return _appConfigData.MaxUsers;

       67             }

       68             set {

       69                 _appConfigData.MaxUsers = value;

       70             }

       71         }

       72 

       73         private InstanceNode _database;

       74 

       75         [Category("Database"), Description("Database to use")]

       76         [Editor(typeof(ReferenceEditor), typeof(UITypeEditor))]

       77         [ReferenceType(typeof(InstanceNode))]

       78         [Required]

       79         public InstanceNode Database {

       80             get {

       81                 return _database;

       82             }

       83             set {

       84                 ILinkNodeService service = (ILinkNodeService)GetService(typeof(ILinkNodeService));

       85                 System.Diagnostics.Debug.Assert(null!=service, "Could not get the ILinkNodeService");

       86                 this._database = (InstanceNode)service.CreateReference(Database, value, new ConfigurationNodeChangedEventHandler(this.DatabaseRemoved), new ConfigurationNodeChangedEventHandler(this.DatabaseRenamed));

       87                 this.Data.Database = _database.Name;

       88             }

       89         }

       90 

       91         [Category("General"), Description("Application state")]

       92         public AppConfigData.AppState State {

       93             get {

       94                 return _appConfigData.State;

       95             }

       96             set {

       97                 _appConfigData.State = value;

       98             }

       99         }

      100 

      101         #endregion

      102 

      103         #region Configuration Tool Event Handlers

      104 

      105         private void DatabaseRemoved(object sender, ConfigurationNodeChangedEventArgs e) {

      106             this._database = null;

      107         }

      108 

      109         private void DatabaseRenamed(object sender, ConfigurationNodeChangedEventArgs e) {

      110             this._appConfigData.Database = e.Node.Name;

      111         }

      112 

      113         #endregion

      114 

      115         #region Private helper methods

      116 

      117 

      118         /// <summary>

      119         /// Used to create a blank database node if none exist in the application.

      120         /// </summary>

      121         private void CreateDatabaseSettingsNode() {

      122             if (!DatabaseSettingsNodeExists()) {

      123                 AddConfigurationSectionCommand cmd = new AddConfigurationSectionCommand(Site, typeof(DatabaseSettingsNode), DatabaseSettings.SectionName);

      124                 cmd.Execute(Hierarchy.RootNode);

      125             }

      126         }

      127 

      128 

      129         /// <summary>

      130         /// Attempts to find a database settings node in the database and returns if it was successful

      131         /// </summary>

      132         /// <returns>True if a DatabaseSettingsNode exist, and False if it doesn't.</returns>

      133         private bool DatabaseSettingsNodeExists() {

      134             DatabaseSettingsNode node = (DatabaseSettingsNode)Hierarchy.FindNodeByType(typeof(DatabaseSettingsNode));

      135             return (null!=node);

      136         }

      137         #endregion

      138 

      139         #region Configuration Manager-related Overrides

      140 

      141         /// <summary>

      142         /// This is called by the framework the each time an instance of this node is created from a previous saved state.

      143         /// This is used to find any dependent nodes in the configuration file and load their values for the user of this node.

      144         /// </summary>

      145         public override void ResolveNodeReferences() {

      146             DatabaseSettingsNode node = (DatabaseSettingsNode)Hierarchy.FindNodeByType(typeof(DatabaseSettingsNode));

      147             System.Diagnostics.Debug.Assert(null!=node, "How is it that the datbase node doesn't exist?");

      148             InstanceCollectionNode instCollNode = (InstanceCollectionNode)Hierarchy.FindNodeByType(typeof(InstanceCollectionNode));

      149             this.Database = (InstanceNode)Hierarchy.FindNodeByName(instCollNode, this._appConfigData.Database);

      150         }

      151 

      152 

      153         /// <summary>

      154         /// This method is called after the main node is created and gives the node an opportunity to add any default child nodes if necessary.

      155         /// </summary>

      156         protected override void AddDefaultChildNodes() {

      157             base.AddDefaultChildNodes ();

      158             this.CreateDatabaseSettingsNode();

      159         }

      160 

      161         /// <summary>

      162         /// This method is called when the configuration node is added to the heirarchy. 

      163         /// Used in this case to set the name of the node, as it is a single-instance node that cannot be renamed by the user.

      164         /// </summary>

      165         protected override void OnSited() {

      166             base.OnSited ();

      167             Site.Name = "Application Configuration";

      168         }

      169 

      170 

      171         /// <summary>

      172         /// Called when the node is renamed by the user.

      173         /// </summary>

      174         /// <param name="e">A <see cref="ConfigurationNodeChangedEventArgs"/> instance, which is used to get the new name of the node.</param>

      175         protected override