Metadata in Managed Applications

As previously discussed, incorporating the use of metadata within software applications allows developers to decouple specific logic detail from the application code and thus promotes more flexible and maintainable application designs. By storing these details in metadata, our programs become inherently more generic and our algorithms more reusable. Additionally, by simply updating the metadata for an application, we can change the way an application works without recompiling or redeploying it.

The .NET Framework provides software developers with a great deal of flexibility and control for storing and retrieving application metadata. The various serialization interfaces, attributes, and classes in .NET afford developers the ability to manage application metadata in formats ranging from XML to binary. This flexibility is provided globally to all applications at the machine level and, more granularly, to specific programs at an application level. Metadata is most often stored using structured XML, written in configuration files according to specific schemas, and is used to modify application behavior at runtime. This design practice ultimately provides lower-cost application maintenance and flexibility, especially in post-release, live production circumstances. It is important to understand the roles and capabilities of the different configuration and metadata storage options that are offered to managed code developers so that you incorporate their use in the design of your application.

  • Machine configuration settings. The .NET Framework allows software developers or system administrators to store configuration metadata that is scoped to all applications on a specific machine in the machine configuration file, or machine.config. This file is located in the run-time installation path in a subdirectory called config. This file contains settings that are related to machine-wide policies and should only be augmented with settings that will apply to all applications running on that specific machine.

  • Security configuration settings. In addition to allowing granular control over machine level settings, the .NET Framework also allows developers or system administrators to modify security configuration files. The security configuration file contains information about the code group hierarchy and permission sets associated with a policy level. Modifying these files manually is not recommended and should be accomplished by using either Caspol.exe or the .NET Framework Configuration Tool (Mscorcfg.msc), which is included in the .NET Framework SDK and can be found in the %windir%Microsoft.NETFramework<Version Number> directory.

  • Application configuration settings. Unlike the machine configuration file, application configuration files, such as web.config, allow developers to store metadata that is scoped to a particular application. These configuration files are used by both Windows client and ASP.NET applications, and they share a common schema across the application models. While these files do contain settings that are pertinent to the Common Language Runtime (CLR) such as assembly binding policy, they also offer flexibility to add custom metadata sections.

Although the .NET Framework provides these three basic types of configuration files, which all share the same basic schema, each of them clearly has different objectives. Even though both machine and security configuration files are critically important to managed applications, in most cases, customized application-specific metadata will be stored within application-level configuration files. We will explore this in greater detail as we move through this chapter.

Application Configuration Files

As suggested by their name, application configuration files contain metadata that is scoped to a particular application. These files are used by all types of managed applications including but not limited to Windows Forms, Console executable, Web service, ASP.NET, and Windows Presentation Foundation (WPF) applications. The name and location of the application configuration files depend on the particular application model being used, but all application configuration files share the same schema and the same metadata storage and access patterns. Let us examine examples of different managed code application types and their respective configuration files.

  • Executable applications. For Windows Forms, console-based, and WPF applications, the configuration file resides in the same directory as the application itself and is accessible by the application at run time. The name of the configuration file is the same as the application but with a .config extension. For example, if the application is called AirCargo.exe, then the corresponding configuration file would be called AirCargo.exe. config. This example is further illustrated by 3-2.

    Executable application and configuration file hierarchy.

    Figure 3-2. Executable application and configuration file hierarchy.

  • ASP.NET applications. For ASP.NET and Web service applications, the configuration files are aptly named web.config and are stored in application’s directory hierarchy. Unlike executable applications, ASP.NET configuration files can exist in multiple locations within the ASP.NET application folder hierarchy. When individual pages are requested, the ASP.NET runtime evaluates all available configuration files and chooses the most appropriate configuration setting based on its physical location relative to the resource being requested. This allows settings stored in the application root directory to be overridden by configuration settings stored with resources in subdirectories. Therefore, the requested page will be affected by the settings stored in the nearest web.config, unless an override exists. This is best explained by considering the following scenarios in conjunction with 3-3. For these scenarios, let us assume that the configuration variables are the same across all web.config files but with different values respectively.

    • Default.aspx. Requests for this page would result in the use of configurations from the web.config file stored in the application’s virtual root directory of http://localhost/AirCargo.

    • Billing.aspx. Any request for this page would result in the use of metadata from the web.config file stored in the virtual directory http://localhost/AirCargo/Billing.

    • CustomerList.aspx. A request for this page would also result in the use of configurations from the web.config file stored in the application’s virtual root directory at http://localhost/AirCargo.

      ASP.NET application and configuration file hierarchy.

      Figure 3-3. ASP.NET application and configuration file hierarchy.

These examples clearly illustrate the relationship of configuration files to both Windows executable, like Windows Forms or Console applications, and ASP.NET applications, as well as how they are respectively managed by the .NET runtime environment. While it is important to understand their behavior, it is much more valuable to understand some of the capabilities that are offered by configuration files within managed code applications. In the next few sections, we will explore the range of options that are available to developers for storing simple or more complex metadata within their applications.

Configuration Settings Basics

Before delving into the specifics of storing application metadata in configuration files, it is important to review the basics of application configuration setting storage within .NET applications. As previously mentioned, all configuration file types adhere to a particular XML schema, which defines the entire range of possible configuration settings for machine, security, and application configuration files. This schema is documented in exhaustive detail on MSDN,[1] and since the scope of this chapter is on leveraging the use of metadata in applications, we will focus on reviewing the schema elements that are germane to application configuration storage.

In application configuration files for all types of managed applications, all XML elements are children of the root element called <configuration>. The first-level child elements that are important for application setting storage are the <configSections>, <appSettings>, and <connectionStrings> elements. The combined role of these elements is to define and represent the sections of the configuration file that store custom application metadata. More specifically, the <configSections> element defines the way in which application settings or metadata will be represented in the configuration file. The <appSettings> and <connectionStrings> elements represent the detailed storage structure for the metadata defined by their respective <configSections> <section> element. The following code snippet illustrates the basic XML structure and syntax of these configuration file elements. This is a generic configuration file. The details of a specific configuration file are presented in the next sections.

<configuration>
<!-- Defines the configuration sections of the config file -->
  <configSections>
    <section name="appSettings"
       type="System.Configuration.AppSettingsSection,System.Configuration, Version=2.0.0.0,
       Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" restartOnExternalChanges="false"
       requirePermission="false"/>
    <section name="connectionStrings" type="System.Configuration.ConnectionStringsSection,
       System.Configuration, Version=2.0.0.0, Culture=neutral
       PublicKeyToken=b03f5f7f11d50a3a" requirePermission="false"/>
  </configSections>
<!-- Represents the structure of the metadata defined in the <configSections>
 element for key/value pair storage -->
  <appSettings>
    <add key="..." value="..."/>
    <add key="..." value="..."/>
  </appSettings>
<!-- Represents the structure of the metadata defined in the <configSections>
element for connectionString storage -->
  <connectionStrings>
    <add name="..." providerName="..." connectionString="..."/>
  </connectionStrings>
</configuration>

As we explore options for storing application metadata, it is important to understand the basic structure of application configuration files and the relationship of configuration section types to the storage structures they represent. The .NET Framework provides a core set of metadata storage structures natively, such as <appSettings> and <connectionStrings>, but also allows developers to add custom structures. Each of these options provides developers with a simple but powerful mechanism to abstract the application metadata away from their code, which allows the application to be much more flexible and adaptable to change. We will examine each of these options in greater depth in the next several sections.

Application Configuration Storage

In the application configuration file, developers can easily store metadata for their applications by simply leveraging the native functionality of .NET. Application settings or other static metadata for the application can be stored in the <appSettings> section of the application’s configuration file. This is accomplished by simply adding child elements to the <appSettings> section that contain key/value pairs representing the application’s metadata. The following code snippet demonstrates the addition of three key/value pairs for an air cargo reservations ASP.NET application, which we will discuss throughout the remainder of this chapter.

<appSettings>
   <add key="MinimumCargoWeight" value="100"/>
   <add key="MaximumCargoWeight" value="10000"/>
   <add key="DefaultAirportCode" value="SEA"/>
</appSettings>

The <appSettings> section supports the addition of three element types, which include the <add>, <remove>, and <clear> elements. The <add> element adds a new application setting, which is identified by a unique key, to the collection. The <remove> element removes a specific setting, based on the unique key, from the collection. The <clear> element allows the removal of all elements that have been previously defined in the <appSettings> section. The <remove> and <clear> elements are often used to remove settings from a higher level configuration file when a hierarchy of configuration files is present within the application, as is often the case with ASP.NET applications. Conversely, these settings are not particularly useful in Windows executable applications, which can only leverage the use of a single configuration file.

In many cases, application developers will be able to leverage this simple key/value pair metadata storage within their application configuration files and realize increased flexibility in their applications. There are several scenarios where this feature provides tangible value for application maintainability and extensibility. Consider the snippet of code shown earlier in the context of our air cargo reservation system example and the values that are represented. You may notice that there is metadata in the <appSettings> section that represents entities like the minimum cargo weight, maximum cargo weight, and default airport code. Some of the values being represented here are likely to be used throughout the application. By storing them in a key/value pair format in a single location and accessing them using a common pattern within the application code, we not only increase the flexibility of the application but isolate and minimize the scope of required testing. This is illustrated in the example code in the next section, which demonstrates the use of a common metadata access class.

Application Configuration Example

In this code sample, a class is created for managing access to common information and utility methods. This is aptly referred to as the Utility class. Inside the Utility class, a static method has been created to obtain application-specific information at run time, using the metadata stored in the application configuration file. This information is retrieved by simply using the standard ConfigurationManager class, which is a part of the Framework Class Library, for reading from the configuration file. By generalizing this method and centralizing it in a common class, the logic can subsequently be shared across the application and evolved as needed. Additionally, if for some reason we decide to change one of these specific configuration values at run time, we will not need to recompile or redeploy the application, because we chose to abstract these details out of the application code.

/// <summary>
/// Class for managing common information and utility methods
/// </summary>

public static class Utility
{
        /// <summary>
        /// Common method for obtaining application metadata
        /// </summary>
        /// <param name="ConfigKey"></param>
        /// <returns>Configuration Value as String</returns>

      public static String GetConfigurationValue(String ConfigKey)
       {
           String value = ConfigurationManager.AppSettings.Get(ConfigKey);
           return value;
       }
 }

Once the common class and access method are available, each ASP.NET page can simply invoke this method to retrieve the specific value defined in the configuration file. Even though this has only been implemented for one specific user interface element, it could very simply be extended to include an entire set of common user interface properties.

lblAircraftMaxWeight.Text = "Minimum cargo weight for this reservation is " +
    Utility.GetConfigurationValue("MinimumWeight") + " lbs.";

Note

Note

We could have just as easily leveraged the ConfigurationManager class directly in our ASP.NET code to access these values. I chose to abstract this from the ASP.NET pages through the Utility class to illustrate the point that centralization provides flexibility. This flexibility would allow additional logic or business rules to be incorporated into the GetConfigurationValue method, rather than be scattered throughout the application.

As demonstrated, storing metadata as simple key/value pairs in an application configuration file under the <appSettings> section is a simple yet effective means of increasing flexibility and reducing complexity of your application. In many cases, this method of storing metadata will provide application developers with the needed flexibility for abstracting the detailed metadata away from the logic of their software applications. However, some applications will be inevitably more complex and require metadata storage mechanisms that will address their needs. Fortunately, managed code applications have additional options for including other types of metadata within the application configuration file like database connection strings or custom XML structures. We will review each of these options in the upcoming sections.

Database Connection String Configurations

It is an accurate assertion to suggest that many managed applications are data driven. After all, data makes applications interesting, useful, and adaptive. Whether those applications are ASP.NET or Windows executable, they may maintain a connection to a database. In fact, in highly distributed environments, applications could maintain connections to multiple databases, perhaps even on multiple platforms. Fortunately, .NET provides application developers with a mechanism for managing the metadata associated with the connections between applications and databases, more commonly known as database connection strings.

As previously discussed, abstracting metadata from application logic increases an application’s flexibility and adaptability to change while promoting the development and reuse of more generic algorithms. Managing logic that enables applications to connect to one or more different data sources is a great example of a situation for which we could apply this design principle. Savvy application developers recognize that hard-coding connection strings in application logic is not flexible or maintainable. Inevitably, some aspect of the relationship between the application and the data source will change after the application is deployed, such as the database credentials or perhaps the location of the database. Simply moving from a development environment to a test or production environment is an example of when target database names will change. By storing these relationships in metadata, application logic can be responsive to changes of this nature without requiring a change to the data access code.

In the .NET Framework, configuration files support a section called <connectionStrings>, which is specifically designed to store connection string information to supplement application logic. Unlike simple key/value pairs, database connection strings serve a specific purpose and deserve to be treated separately. Not only are they critically important for data-driven applications, but often times they contain information such as user credentials that is more sensitive than information stored in the general-purpose <appSettings> section and may be desirable to encrypt. The following code snippet is a good example of the <connectionStrings> section of the application configuration file being used to store connection strings for multiple local Microsoft Access databases.

Note

Note

Although this book does not discuss protecting metadata using some of the encryption features provided by the .NET Framework, I recommend reviewing Dino Esposito’s explanation in his book Programming Microsoft ASP.NET 2.0 Core Reference (Microsoft Press, 2005), which covers the subject very well.

<connectionStrings>
    <add connectionString="Provider=Microsoft.ACE.OLEDB.12.0;Data
       Source=C:myFolderAircraftDB.accdb;Persist Security Info=False;"
       name="CargoAircraftDB"/>
    <add connectionString="Provider=Microsoft.ACE.OLEDB.12.0;Data
       Source=C:myFolderCustomerDB.accdb;Persist Security Info=False;"
      name="CargoCustomerDB"/>
</connectionStrings>

Similar to the functionality of the <appSettings> section mentioned previously, <add>, <remove>, and <clear> can be used to maniplate the contents of the <connectionStrings> section. Additionally, each connection string node is identified by a name and can be accessed programmatically using the ConfigurationManager class. This is demonstrated in the following code sample, where we extend the aforementioned Utility class to include a generic method for returning a database connection object.

Using System.Data.Odbc;

public static OdbcConnection GetDatabaseConnection(String datasourcename)

{

  OdbcConnection conn = new
  OdbcConnection(ConfigurationManager.ConnectionStrings[datasourcename].ConnectionString);

  return conn;

}

Storing database connection metadata in the <connectionStrings> section clearly helps developers abstract the relationship of the application and the database away from the application logic, thus increasing flexibility of the application. The .NET Framework recognizes the uniqueness of this pervasive programming necessity and provides a specific section for managing the information within the application configuration file. While this feature has provided application developers with out-of-the-box convenience, the need to have specific configuration sections goes well beyond database connection strings. As we will review in the next section, the .NET Framework also provides a great deal of flexibility for application developers to define and construct custom metadata storage structures within the application configuration file and strongly typed classes for accessing the information.

Custom Configurations

Thus far, we have explored some basic examples of storing application metadata using the standard configuration options like key/value pairs within the <appSettings> section or database connections strings within the <connectionStrings> section. Although these methods are in and of themselves quite flexible and valuable, there are other potential solutions that would allow storage of more complex metadata structures within your application. Those solutions include embedding a custom XML structure within the application configuration file and specifying a custom configuration section and associative configuration handler or using an XML file that stores desired data structure. Both options would essentially meet the basic needs, but it’s worth examining the benefits of each solution prior to deciding which will work best for your application.

  • Defining a custom configuration section and handler. As compared with using simple XML files to store application metadata, custom configuration sections provide similar flexibility in terms of metadata storage capabilities. Application developers can define a tailor-made configuration section that represents a custom XML metadata structure. Additionally, they can leverage the native support of the System.Configuration classes for optimally accessing the data using custom configuration section handlers. The downside to storing metadata in application configuration files is that they require application restarts for changes to take effect. In most cases, this is not a major concern, which makes this metadata storage option quite appealing.

  • Plain XML file. Using a simple XML file to define custom metadata structures is certainly a flexible and powerful option. This method provides complete freedom over storage and access patterns and does not force or require application restarts when changes are applied. The downside is that application developers will need to write all of the metadata handling code, and there are obviously no guarantees that this custom code will be as optimized as the more general purpose System.Configuration classes.

As a basic rule, leveraging the metadata storage options provided inherently within the .NET Framework should always be the first option to consider. Storing application metadata using simple XML files is certainly more flexible from a programming perspective. However, defining custom configuration sections and handlers provides application developers access to the general-purpose, more optimal System.Configuation classes for accessing application metadata. This allows developers to write far less code within their applications and subsequently to reduce the risk of introducing bugs into their metadata access and handling logic. The following example, building upon the previously discussed air cargo reservation system, explores storing more complex metadata and implementing custom configuration sections and handlers to access that metadata.

Custom Configuration Section Example

In the previous examples, we explored storing simple key/value pair formatted metadata using the standard configuration options available in the <appSettings> section of the application configuration file. While this worked well for storing some unstructured bits of metadata, it did not allow us to store much more than a single value for each item. Suppose we wanted to store information about the aircraft that the air cargo company has available in its inventory. Our first inclination might be to store this information in a database. Even though this may be a valid choice in certain application designs, the solution is overkill for this scenario. Let us assume that a custom configuration section is all that is required, so we create a structure that resembles the following XML and its associative <configSection> element.

<!--Define the configuration section -->
<configSections>
    <section name="Aircraft" type="AircraftConfiguration"/>
</configSections>
<!-- Define the custom metadtata structure -->
<Aircraft>
        <Inventory>
          <add ID="N443BC" type="Lockheed L188C Electra" availability="true"
          inService="true" category="MEL" year="1977"/>
          <add ID="N122EC" type="Merlin 4A" availability="true" inService="true"
          category="MEL" year="1975"/>
        </Inventory>
</Aircraft>

The custom configuration section that has been defined in this example is called "Aircraft" and is established as a section within the <configSections> element of the configuration file. This configuration section name must match the subsequent XML metadata structure’s root node name in order to function properly. Additionally, the associative configuration handler is named by the type attribute of the AirCraftConfiguration section and denotes the class that will handle the processing of the custom section. This markup helps to establish the custom configuration section that stores the metadata within the application configuration file. To be accessible to the application code, though, supporting classes are required.

Once the application’s configuration metadata is structurally defined, application developers must subsequently create the appropriate supporting classes to plug into the configuration plumbing provided by the .NET Framework. This begins by defining a class similar to that of the class denoted in the previous markup as AircraftConfiguration. This class, which derives from .NET’s ConfigurationSection class, represents a deserialized object representation of the configuration section Aircraft. This class provides one property called Inventory, which ultimately provides access to a collection of the items defined in the Inventory node of the configuration metadata, and it provides one static method called GetConfig(), which encapsulates code for initializing access to the <Aircraft> configuration section. This is illustrated with the following code.

public class AircraftConfiguration : ConfigurationSection
{
    /// <summary>
    /// Returns an AircraftConfiguration instance – this is provided to optmize access
    /// to the configuration section but is not required.
    /// </summary>
    public static AircraftConfiguration GetConfig()
    { return ConfigurationManager.GetSection("Aircraft") as AircraftConfiguration; }

    // Returns the Aircraft collection object which is a collection of aircraft elements.
    [ConfigurationProperty("Inventory")]
    public AircraftCollection Inventory
    {
        get { return this["Inventory"] as AircraftCollection; }
    }
}

In this class, the ConfigurationProperty attribute that is decorating each property indicates that these properties are specified in the custom configuration metadata that was previously defined. The ConfigurationProperty attribute accepts the following parameters.

  • Name. Specifies the name of the property as described by the custom configuration metadata in the configuration file.

  • DefaultValue. Represents the default value of the property if it is not specified in the configuration metadata.

  • IsRequired. Illustrates whether the property is required or optional. If this is set to true and the property is not specified in the configuration file, an exception will be thrown.

  • IsKey. Indicates whether a ConfigurationProperty is the key for the containing ConfigurationElement object.

  • IsDefaultCollection. Specifies whether the property is the default collection of an element.

In this example, only name is defined in the ConfigurationProperty attribute, which is acceptable since the Inventory accessor returns a collection of Aircraft objects. These objects are created inside the AircraftCollection class, which derives from the ConfigurationElementCollection class and manages the deserialization of the elements beneath the Inventory node in the configuration file. When extending the ConfigurationElementCollection class, application developers must override the CreateNewElement and GetElementKey methods, which are responsible for creating the new element in the ConfigurationElementCollection class and providing an element key respectively. In this case, the XML attribute ID from the custom metadata section in the configuration file is mapped as the key for each element in the collection. This is illustrated within the following code.

/// <summary>
/// AircraftCollection class represents the collection of elements associated with the
/// <inventory> element of the custom configuration section.
/// </summary>
public class AircraftCollection : ConfigurationElementCollection
{
    public Aircraft this[int index]
    {
        get
        { return base.BaseGet(index) as Aircraft; }
        set
        {
            if (base.BaseGet(index) != null)
            { base.BaseRemoveAt(index); }
            this.BaseAdd(index, value);
        }
    }

    /// <summary>
    /// override the GetElementKey method which maps an xml attribute from the
    /// configuration file to the key of the collection.
    /// </summary>
    protected override object GetElementKey(ConfigurationElement element)
    { return ((Aircraft)element).Id; }

    /// <summary>
    /// override the CreateNewElement method so the new Aircraft class is intialized.
    /// </summary>
    protected override ConfigurationElement CreateNewElement()
    { return new Aircraft(); }
}

As previously described, the AircraftCollection class returns a collection of Aircraft objects, which should represent the deserialized version of the XML elements defined as children of the <Inventory> element in the application configuration file. As such, the Aircraft class derives from the ConfigurationElement class, and each of its properties should appropriately map to the attributes of the XML element in the configuration file. The abridged class definition for the Aircraft object is described below and illustrates how the attributes of the XML are exposed as properties of the class.

    /// <summary>
    /// Aircraft class that represents the Aircraft configuration element which is a child to
    /// the <inventory> element within the custom configuration section.
    /// </summary>
    public class Aircraft : ConfigurationElement
    {

    // Return the "Id" attribute
    [ConfigurationProperty("Id", IsRequired=true)]
    public string Id
    { get { return this["Id"] as String; } }

    // Return the "Category" attribute
    [ConfigurationProperty("Category", IsRequired = false)]
    public string Category
    { get { return this["Category"] as String; } }

    // Return the "Type" attribute
    [ConfigurationProperty("Type", IsRequired = false)]
    public string Type
    { get { return this["Type"] as String; } }
...
}

Now that the custom application metadata for the air cargo reservations application has been defined in the application configuration file and the supporting AircraftConfiguration, AircraftCollection, and Aircraft classes have been defined, pulling it all together is fairly easy. To illustrate how this configuration metadata can be leveraged within the application’s user interface pages, we will wire up a simple dynamic drop-down list box in an ASP.NET page. Once the DropDownList object is added to the ASP.NET page, the following code illustrates how to dynamically populate it using the custom configuration metadata, which was previously defined.

if (!Page.IsPostBack)
{
    // Retrieve the Aircraft inventory from config and add them to the drop down list
    foreach (Aircraft aircrft in AircraftConfiguration.GetConfig().Inventory)
    {
    this.ddlAircraft.Items.Add(new ListItem(aircrft.type + " - " + aircrft.Id,
    aircrft.Id));
    }
}

The result of this example is an ASP.NET application whose user experience is driven by custom-defined application metadata stored in the application’s configuration file. By leveraging metadata storage options in the <appSettings> configuration section as well as a custom configuration section, the air cargo reservation system’s user experience can be changed at any time by simply updating the web.config file that is stored within the application. This flexibility allows future adjustments to the metadata of the application without recompiling or redeploying the application. A sample screen is provided in 3-4, which is the runtime result of the examples defined previously.

Northwest Air Cargo Reservations System default screen.

Figure 3-4. Northwest Air Cargo Reservations System default screen.

Application Configuration in Practice

In addition to the high degree of flexibility that application configuration files provide developers for storing metadata, the ability to add and update configuration settings in post-release, live production circumstances is equally powerful. With both Windows executable and ASP.NET applications, revising configuration files after an application is compiled and deployed can be accomplished with minimal impact to the running application. In the case of ASP.NET, any change to the web.config files will cause the application domain, which is the virtual address space in which the application is executing, to restart. This will actually result in user sessions being terminated. Once this occurs, the new settings will take effect. Alternatively, with Windows executable applications, changes to the configuration files will take effect when the application restarts. Leveraging this capability within your software applications will clearly help increase the overall quality of the application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.47.166