Objectify an XML Node with an IConfigSectionHandler
The areas of interest are in boldface. The first is the FtpSettings class. Create returns an object that is the base type for all .NET classes; thus, the IConfigurationSectionHandler.Create method can be used for any type. The FtpSettings class itself is little more than a data structure, and the GetFormatted method is really for debugging purposes only.
The FtpSettingsSectionHandler uses the implements keyword to indicate that a contract exists between this class and the IConfigurationSectionHandler interface. The Create method satisfies the contractual requirement.
The static method ConfigurationSettings.GetConfig called with an "ftpSettings" argument would implicitly invoke the Create method. Passed to Create are three arguments:
- Settings in a corresponding parent section
- An instance of HttpConfigurationContext or reserved
- XmlNode representing our custom section
Ultimately, what you want to do is create an instance of your target type, read each node and child node, and populate the fields of your target type, FtpSettings.
The first statement in Create instantiates an instance of your target type, FtpSettings. If the argument section is null, the caller gets a default instance of the objectified section. Because you have default configuration settings, this might be suitable. It at least ensures that callers get an instance of the FtpSettings rather than null.
Next, you need to read attributes of each node, child nodes and their attributes, and so on, recursively. Your <ftpSettings> section is nested only one child deep, so that is all you will read. Anything not specifically looked for in your section handler will be ignored. The first attribute you read is the mode. This is useful if you wanted to enable or disable an FTP process post-deployment. After you read the parent node, you need to read each child node. As demonstrated by the code, you literally examine the node name; if the node name matches a name of interest, you read that node's "value" attribute.
Note: Don't let the GOTO statement bother you. Decades ago, GOTO (branch) statements were all the rage. Then, enlightened scholars decided that arbitrarily located branch statements were a blight on comprehensible code. The truth, as is with all things, lies somewhere in between. Ultimately, code is converted to machine language, which is riddled with branch statements (je, jne, jmp, and many more). If used with care, and perhaps sparingly, there is nothing wrong with an occasional GOTO statement. However, GOTO is not a carte blanche device intended to support spaghetti code.
Because you know the expected type of each node, you can perform type conversions and handle invalid types. For example, the remote port is typecast to an integer type. In the catch block, you convert any generic exception to a ConfigurationException and alert the caller about a problem in the .config file. Assuming no exception occurs, you return the fully instantiated FtpSettings object at the end of the try block.
Reading the Persisted Object
To read the object represented by a custom configuration section, you need to call ConfigurationSettings.GetConfig and pass the case-sensitive name of the XML section. The return type is an object type. In Visual Basic, you can assign an arbitrary object to a specific type, but it is preferable to typecast the generic object type to the specific type represented by the configuration section. Listing 3 shows the weakly typed implicit conversion supported by Visual Basic .NET first, followed by the more explicit example required by non-VB programmers.
Listing 3: Reading and instantiating persisted configuration objects.
Imports FtpHelpers Imports System.Configuration Module Module1 Sub Main() Dim settings As FtpSettings = _ ConfigurationSettings.GetConfig("ftpSettings") Console.WriteLine(settings.GetFormatted()) Console.ReadLine() Dim settings2 As FtpSettings = _ CType(ConfigurationSettings.GetConfig( _ "ftpSettings"), FtpSettings) Console.WriteLine(settings.GetFormatted()) Console.ReadLine() End Sub End Module
Finally, if your project or module is configured with Option Strict On, the first chunk of code will not work. Implicit type conversion does not work in strict mode.
A Judgment Call
Beginner computer programmers are given a bunch of rules: Do this, but don't do that. As one's skill increases, an understanding of the origin of the rules grows. Finally, one learns to bend—and even break—the rules, and understands when doing so is permissible. A good example is the GOTO statement. This is called judgment.
Judgment is that quality that helps you decide when existing code is sufficient and when you have to write custom code. If you are not sure of your own judgment, use the AppSettings property to read externalized values. If you have a reason for doing so, write your own .config section handler. This article has shown you how.
The problem is judgment is subjective. If your judgment leads you to roll your own section handler, which takes longer and interferes with your schedule, your judgment, while technically superior, may be called into question. Existing code is often quicker and less likely to induce bugs in the short term—and the short term is where many business people and managers dwell.
Paul Kimmel is the VB Today columnist, a Visual Developer MVP, the president of Lansing, Michigan's .NET Users Group, and has written several books on object-oriented programming. Paul is the chief architect for Software Conceptions, Inc., founded in 1990. You may contact him at email@example.com if you need assistance developing software or are interested in joining, presenting at, or sponsoring the Lansing Area .NET Users Group (www.glugnet.org).
Copyright © 2004 by Paul Kimmel. All Rights Reserved.
Page 2 of 2