The Enterprise Library Configuration Tool is really just a Windows Form application that uses the design-time features in the Configuration Application Block. The Configuration Application Block's design-time features are based on the same concepts and architecture as the design-time features for .NET Windows Forms and ASP.NET applications: the classes in the System.ComponentModel namespace. Exposing the Configuration Application Block's design-time features has many benefits. It provides a single, consistent model that all application blocks can use to display, validate, and update the configuration data for their runtime components. It also gives you design-time configuration capabilities for adding your own StorageProviders, Transformers, application blocks, and extensions to application blocks. Figure 2.17 depicts several concepts. First, it illustrates how the Enterprise Library Configuration Tool uses the design-time features in the Configuration Application Block to achieve its needs. It also emphasizes that the Configuration Application Block's design-time objects rely on its runtime objects for saving and retrieving configuration information. Lastly, it shows how client applications that are built to use Enterprise Library will either directly or indirectly use the Configuration Application Block's runtime features for storing and retrieving either their own custom configuration information or the configuration information needed for the Enterprise Library application blocks to function properly. Figure 2.17. High-Level Design of the Configuration Application Block's Design-TimeThe following sections describe the concepts behind the Configuration Application Block's design-time and how it uses the classes contained in the System.ComponentModel namespace. They include a discussion of how you can use the design-time features in the Configuration Application Block to provide new design-time features in the Enterprise Library Configuration Tool and walks you through an example of creating such an extension. Using the System.ComponentModel NamespaceEnterprise Library's Configuration design-time is based heavily on the .NET System.ComponentModel namespace. This namespace includes base classes and interfaces for implementing attributes and type converters, binding to data sources, licensing components, and more. The namespace provides Enterprise Library with a valuable service-based model that is not only used to implement its runtime and design-time behavior for components and controls, but it also allows Enterprise Library's design-time experience to be extended. Some of the interfaces used by the Enterprise Library Configuration design-time are IComponent, IContainer, ISite, IServiceProvider, and IServiceContainer. The IComponent InterfaceThe IComponent interface defines the functionality that must exist for all components. The System.ComponentModel.Component class is the default implementation of this interface and is the base class for all components in the common language runtime that marshal by reference. Components are especially important because they can access services from a container that owns the component. In Enterprise Library, the abstract Configuration-Node classand therefore any class that derives from Configuration-Nodeis a component. Figure 218 illustrates how the ConfigurationNode class derives indirectly from System.ComponentModel.Component and shows just a few of the concrete ConfigurationNode classes (e.g., XmlIncludeTypeNode, StorageProviderNode, TransformerNode, and ApplicationConfigurationNode) that derive from this base class. Figure 2.18. ConfigurationNode Derives From ComponentConfiguration nodes provide the design-time representation of configuration settings and do the following.
The left pane of the Enterprise Library Configuration Tool displays the configuration nodes in a configuration tree (see Figure 2.19). When a user selects a configuration node, the collection of settings associated with that configuration node display in the Property pane. Figure 2.19. Configuration Nodes and the Property PaneWhen an application's configuration is created or opened, the Enterprise Library Configuration Tool first creates an application configuration node and adds it to the SolutionConfigurationNode, the top-level node to which all other nodes are attached. The SolutionConfigurationNode is always named Enterprise Library Configuration. The application configuration node is the parent for all application block nodes that are added to that application's configuration. Child nodes underneath the application node are associated with application blocks. Figure 2.19 shows an application that is configured to use the Data Access Application Block; therefore, the application node is the node labeled Application1 and the Data Access Application Block is a child node. Because all application blocks depend on the Configuration Application Block, a node for the Configuration Application Block also appears as a child to the application node. Configuration nodes have properties that allow them to be maintained in parent-child relationships with other nodes. Each node can have at most one parent node, but it can have multiple child nodes. The parent-child relationship of nodes allows configuration settings to be organized in a logical hierarchy at design time. The Enterprise Library Configuration Tool uses this hierarchical relationship to construct the configuration tree. All configuration nodes derive from the abstract ConfigurationNode base class. A ConfigurationNode's public properties are exposed and editable at design time as properties displayed within the node's Property pane. In Figure 2.19, the DatabaseInstance node is referencing a DatabaseType node and a ConnectionString node. Clicking the plus sign next to the node's settings will let you edit the settings for the node. The Name property is also exposed and editable for this node. Every configuration node includes the public property Name, which represents the name of the node that is displayed in the tree view. The IContainer InterfaceThe System.ComponentModel.IContainer interface defines the functionality that must exist for a container, an object that encapsulates and tracks zero or more components. The IContainer interface defines the properties and methods that must exist to wrap around a collection of components. The Components property returns a ComponentsCollection; the Add and Remove methods allow components to be added to and removed from the collection. The Container class is the default implementation of the IContainer interface. In addition to the Components property and the Add and Remove methods, the Container class also provides a CreateSite method for creating a Site (explained next) for a given component in the container, and a GetService method for retrieving a service object for a specified type. By using a container in an application, application-wide services can be retrieved throughout the entire application. In Enterprise Library, the ConfigurationDesignHost class implements the IContainer interface and is the container for all the classes that derive from ConfigurationNode. Figure 2.20 shows the ConfigurationDesignHost's properties and methods. Figure 2.20. Properties and Methods of ConfigurationDesignHost
The ISite InterfaceA site "glues" a component to the container it lives in and enables communication between the two. The ISite interface defines the properties needed to enable service retrieval by a component. In Enterprise Library, the ConfigurationNodeSite class implements the ISite interface. It binds configuration nodes with the ConfigurationDesignHost. Figure 2.21 shows the properties and methods for the ConfigurationNodeSite. Two notable properties are the Component and Container properties; these are used to bind the two together. Figure 2.21. Properties and Methods of ConfigurationNodeSite
The IServiceProvider and IServiceContainer InterfacesThe IServiceProvider and IServiceContainer interfaces derive from the System namespace. A service object is an object that provides custom support to other objects. Services are powerful because they allow for a loose coupling between the interface that defines a service and the classes that implement it. Components can request a service and retrieve an instance that they can use, but they never need to know anything about the implementation of the service. The IServiceProvider interface defines a mechanism for retrieving a service object. A service container is, by definition, a service provider. In addition to providing services, it also provides a mechanism for adding and removing services. To obtain a service at design time, the GetService method of a component sited in design mode can be called. Designers and other objects can add or remove services at design time as well. When a service is added, it can be added with instructions to promote it. When a service is promoted, it is added to all parent service containers until the top of the service container tree is reached. This allows a designer to provide a global service that other objects in the process can use. Enterprise Library's abstract ServiceContainer class implements both the IServiceContainer and IServiceProvider interfaces. The ConfigurationDesignHost derives from the ServiceContainer class; thus, it is also a ServiceContainer. Figure 2.22 shows the ServiceContainer's methods. Notice the methods that exist for adding, getting, and removing services. Figure 2.22. Properties and Methods of ServiceContainer
The ConfigurationDesignHost is created when the Enterprise Library Configuration Tool starts, and it acts as the hub for design-time configuration-related objects and services. As previously mentioned, this class is a container for configuration nodes and design-time services. As configuration nodes are added to an application's configuration, the ConfigurationDesignHost object creates an object of type ConfigurationNodeSite for the nodes. At this point, the component is known to be sited and is fully functional. Since the ConfigurationNodeSite object implements the ISite interface, it lets the configuration node access the services hosted by the ConfigurationDesignHost object. Table 2.3 lists the services initialized by the ConfigurationDesignHost object when it is created.
Because a configuration node can access the container via the Site property that has been set for it, the node can perform actions requiring services by calling Site.GetService(Type). IServiceProvider.GetService(Type) allows dynamic retrieval of services from the container. Thus, new configuration nodes can be created and dynamically added to the ConfigurationDesignHost that can access and use the services provided by the Enterprise Library's Configuration design-time. Configuration HierarchiesConfiguration hierarchies maintain a logical structure and grouping for configuration settings. Configuration nodes can have a parent node, child nodes, and sibling nodes. Every configuration hierarchy has a single root node. Hierarchies must conform to the IUIHierarchy interface. The IUIHierarchy interface can prove useful because it has methods to search for nodes either by node type or by name. This becomes especially important when a configuration node needs to reference another configuration node in an application. For example, Figure 2.19 shows a DatabaseInstance node for the Data Access Application Block referencing a ConnectionString node and a DatabaseType node. The IUIHierarchy interface provides this configuration node with the capability to return a list of the types of these nodes (i.e., ConnectionString node and DatabaseType node) that have already been configured for the application. Configuration Menu Items and CommandsConfiguration menu items and commands provide the user interface for launching configuration-related actions at design time. For example, as depicted in Figure 2.23, a command on the Data Access Application Block ConnectionStrings configuration node lets users create new connection strings as child nodes. Figure 2.23. Configuration Menu Items and CommandsActions are exposed as configuration menu items. Configuration menu items are classes that derive from the ConfigurationMenuItem class. Common menu items that exist for many configuration nodes are New, Rename, Remove, Validate, New Application, and Save Application. You can add custom menu items to a configuration node by overriding the OnAddMenuItems method. A configuration node command determines the action performed when a user selects a menu item. The Enterprise Library Configuration design-time follows the command design pattern[1] in its implementation for commands. The Configuration Application Block includes an abstract ConfigurationNodeCommand base class from which all concrete Command classes must derive. The base class defines an Execute method that gets called when the command is executed. However, this method passes control to a derived class' implementation of the virtual ExecuteCore method.
For example, the AddChildNodeCommand command adds a configuration node as a child of the currently selected configuration node. The logic for the actions that take place when this command is executed resides in the AddChildNodeCommand's ExecuteCore method. You can add new commands to the design-time experience by deriving a new class from the ConfigurationNodeCommand class and implementing the ExecuteCore method. Figure 2.24 shows a small sampling of the concrete commands that exist as part of the Configuration Application Block's design-time features. Figure 2.24. ConfigurationNodeCommand HierarchyThe ConfigurationDesignManager ClassThe ConfigurationDesignManager provides the connection point between the Enterprise Library Configuration Tool and an application block's design-time support. The ConfigurationDesignManager creates menu items and commands that are invoked as a user interacts with the Enterprise Library Configuration Tool to configure an application block, and it is responsible for loading and saving configuration data. The ConfigurationDesignManager is also the class that provides the capability for new application blocks to be "snapped into" the Enterprise Library Configuration Tool. The Configuration Tool discovers classes that implement the IConfigurationDesignManager interface by examining the assembly attributes for assemblies that it knows about, instantiating the type defined in the ConfigurationDesignManagerAttribute, and validating the type as an implementation of IConfigurationDesignManager. The IConfigurationDesignManager interface defines four methods: Register, Open, Save, and BuildContext. Table 2.4 details the significance of each of these methods.
|