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'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.
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.
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