. NET application framework architecture design practices-provides diverse configuration methods for Application Frameworks

Source: Internet
Author: User
Tags visual studio 2010

Microsoft. NET is an applicationProgramDevelopers provide a wide range of programming models and types for processing configuration data. With these components, developers and users can easily intervene in application execution behaviors and results by setting configuration data without re-compiling the application, so that the same application can meet the special requirements of different application scenarios without changing the source program. In most cases, developers also need to be able to configure the application framework to obtain different functional features of the framework. For example, we will.. NET application configuration file, if the application still uses Microsoft patterns & Practices Enterprise Library (entlib, you also need to add configuration data for entlib so that different frameworks can meet the normal operation needs of applications. Therefore, reading, managing, and using configuration data is a required function of each application framework.

Here, we do not intend to further elaborate on how Microsoft. Net-based applications read and manage configuration data. Done. NET Application Development readers know that the configuration data is written in the application configuration file, such as app. config or web. config, and then use system. the classes provided in the configuration namespace can read the configuration file to obtain configuration data. You can also use the configurationsection, configurationelement, and other classes to define the configuration data structure of the application. For more information, see related documents or webpage materials. It must be noted that the app. config or web. config is also based on appdomain, specifically: In executable applications, app.configto .exe. config or. DLL. the extension form of config, which is in the same directory as the main program. In a web application. config is under the root directory of the virtual directory. Of course, these are general cases. In fact,. Net allows developers to change the file name of APP. config or web. config.

Now, it is very interesting to simply consider the implementation of a single test tool. The simplest way is to provide some attributes in this standalone test tool for specifying the test Type and test method, for example, testclassattribute, testinitializeattribute, and testmethodattribute in Microsoft Visual Studio 2010 standalone testing framework, and then create a class library for standalone testing, use these attributes in this class library to define the test Type and test method. Next, you will use a test tool based on Windows Forms or WPF to load this class library, read all the test types and test methods in the class library through. Net reflection technology, and execute these methods. In the execution process, use try... Catch captures exceptions from assert to determine whether the test is successful. This process seems simple, but there are still many details that need to be carefully considered. The most important problem is the configuration file. If you want to test only some numerical operations orAlgorithmCongratulations, you don't have to worry about this configuration file. But if you need to test an application framework, and the running of this framework depends on some configuration data, then you will have a headache: Where should I write the configuration data? Is it written in the configuration file of this test tool? It must be unreasonable. Otherwise, you need to modify the configuration file of the test tool before each test. Should I write it in the configuration file of the class library? Sorry, this does not work, because the test tool will load the class library to its appdomain, so the class library won't be able to read its own configuration file.

To solve this problem, you need to change the Loading Method of the test tool to the class library, which is converted from the simple assembly. Load Method to load it into another appdomain. When creating this new appdomain, use appdomainsetup. configurationfile to set the configuration file, similarCodeAs follows:

Private void loadremoteproxy () {appdomainsetup ads = new appdomainsetup (); ads. applicationbase = path. getdirectoryname (this. assemblyfile); ads. applicationname = path. getfilename (this. assemblyfile); string configfilename = This. assemblyfile + ". config "; if (file. exists (configfilename) {ads. configurationfile = configfilename;} ads. disallowbindingredirects = false; ads. disallowcodedownload = true; Ads. shadowcopyfiles = "true"; ads. shadowcopydirectories = environment. getfolderpath (environment. specialfolder. internetcache); clientdomain = appdomain. createdomain (guid. newguid (). tostring (), null, ADS); object [] ARGs = new object [] {This. assemblyfile, typeof (testclassattriof), typeof (classinitializeattribute), typeof (classcleanupattribute), typeof (testinitializeattribute), typeof (testcleanupattr Ibute), typeof (testmethodattribute)}; iremoteproxy P = (iremoteproxy) clientdomain. createinstanceandunwrap (typeof (iremoteproxy ). assembly. fullname, "visualbenchmark. proxies. remoteproxy ", false, 0, null, argS, null, null); If (P! = NULL) {This. assemblyname = P. assemblyname; this. Proxy = P ;}}

 

This Code comes from visual benchmark, a performance comparison tool based on standalone tests that I wrote earlier. The tool's home page is:Http://visualbenchmark.codeplex.com. Interested readers can obtainSource codeFor further reference.
The focus of our discussion here is not the single test tool, but the tested framework. By loading the tested framework to a separate appdomain, we can make it successfully read the configuration data during the testing process. However, the problem arises again: if we need to test the different behaviors of the tested framework on different configuration data in the same test, but the configuration file can only have one, how can we implement such a test requirement? The answer to this question is: we should provide a variety of configuration methods for the application framework, not just support the configuration file.

Design

In use. when developing the application framework with net technology, we usually design a configuration data structure, which is represented by classes inherited from configurationsection, configurationelement, and configurationelementcollection in the code, then, in the application configuration file, you can easily use the corresponding XML tag (TAGS) to represent the configuration data. This content should be very familiar to a senior. Net developer. It can be said that ,. the configuration file in. NET is the most basic and common configuration data solution, so when we want to provide a variety of configuration methods for the application framework, this configuration file-based approach has become an indispensable choice. In addition, we can also provide configuration methods based on the characteristics of the framework, such as other XML files, databases, or code.

To enable the framework to support configuration files and other configuration methods at the same time, we need to unify these different configuration methods in design. As analyzed above, the configuration file method is essential. Therefore, we can make these methods transparent to the framework, however, the framework only perceives that only configuration files are currently available. In other words, when configuration data is required during the implementation of the Framework, the Framework Code only uses the types inherited from configurationsection, configurationelement, and configurationelementcollection.

First, defineConfiguration SourceConfiguration source interface.Configuration section(Configuration section), because the configuration section containsConfiguration element(Configuration element) andConfigure Element SetConfiguration element collection. Therefore, the configuration section is actually the aggregation root of the whole configuration information aggregation. Then, for different configuration methods, create a class that implements the configuration source interface, and initialize the configuration section object in different ways in these classes. For example, you can use the system. configuration. configurationmanager can read the configuration section in the configuration file, or access the database to obtain configuration data. Finally, the framework only needs to obtain the specific implementation of the configuration source in the form of dependency injection to obtain the configuration data. The following class diagram shows the participants of the design and Their Relationships. to simplify the description, this class diagram only contains two configuration implementation methods based on the configuration file and source code.

The iconfigurationsource interface in is the configuration source interface. configfileconfigurationsource and regularconfigurationsource are two classes that implement this interface. The configfileconfigurationsource class uses the standard one. the. NET Configuration System reads the configuration section from the configuration file, while the regularconfigurationsource provides the caller with public methods such as setelement and addelement, so that developers can directly call these methods in code to provide configuration data to the framework. The application class of the application framework receives the specific implementation of iconfigurationsource In the constructor to initialize its configsource attribute. Applicationfactory is a singleton class. Its createapplication static method also receives the specific implementation of iconfigurationsource to create an application instance. At the same time, applicationfactory exposes the currentapplication attribute, in this way, you can obtain the currently running application instance anywhere in the framework, and then obtain the configuration source defined in the application instance.

Using this design makes it easy to provide new configuration methods for the framework. For example, if we want the framework to read configuration data from the database, we only need to create a databaseconfigurationsource class to implement the iconfigurationsource interface. In this class, you can set the config attribute by accessing the database.

Implementation

Assume that the configuration section of a framework is defined as follows:

Using system. configuration; public class applicationelement: configurationelement {[configurationproperty ("provider", iskey = true, isrequired = true)] Public String provider {get {return (string) base ["provider"] ;}set {base ["provider"] = value ;}} public class objectcontainerelement: configurationelement {[configurationproperty ("provider", iskey = true, isrequired = true)] Public String provider {get {return (string) base ["provider"] ;}set {base ["provider"] = value ;}}} [configurationcollection (typeof (objectcontainerelement), additemname = "objectcontainer", collectiontype = configurationelementcollectiontype. basicmap)] public class objectcontainerelementcollection: configurationelementcollection {protected override string elementname {get {return "objectcontainer" ;}} public override configurationelementcollectiontype collectiontype {get {return configurationelementcollectiontype. basicmap ;}} protected override configurationelement createnewelement () {return New objectcontainerelement ();} protected override object getelementkey (configurationelement element) {return (element as objectcontainerelement ). provider;} public void add (objectcontainerelement element) {This. baseadd (element) ;}} public class frameworkconfigsection: configurationsection {[configurationproperty ("application", isrequired = true)] public applicationelement application {get {return (applicationelement) base ["application"] ;}set {base ["application"] = value ;}} [configurationproperty ("objectcontainers")] public objectcontainerelementcollection objectcontainers {get {return (objectcontainerelementcollection) base ["objectcontainers"];} set {base ["objectcontainers"] = value ;}}}

 

According to the above design analysis, the configuration source implementation is as follows:

Public interface iconfigurationsource {frameworkconfigsection config {Get ;}} public class configfileconfigurationsource: iconfigurationsource {private frameworkconfigsection config; Public configfileconfigurationsource () {This. readfromconfigfile ();} private void readfromconfigfile () {This. config = (frameworkconfigsection) configurationmanager. getsection ("frameworkconfig");} public frameworkconfigsection config {get {return this. config ;}} public class regularconfigurationsource: iconfigurationsource {private frameworkconfigsection config; Public regularconfigurationsource () {Config = new frameworkconfigsection (); config. application = new applicationelement (); config. objectcontainers = new objectcontainerelementcollection ();} public void addelement (objectcontainerelement element) {config. objectcontainers. add (element);} public void setelement (applicationelement element) {config. application = element;} public void addobjectcontainer (string provider) {config. objectcontainers. add (New objectcontainerelement {provider = provider});} public void setapplication (string provider) {config. application = new applicationelement {provider = provider};} public frameworkconfigsection config {get {return this. config ;}}}

 

In the Framework,Guide(Bootstrapper) to start services on which some frameworks depend or to prepare data. To simplify the description, we only use the application and applicationfactory described above to simulate this feature of the Bootstrap. First define the iapplication interface, and then create a class application that implements this interface:

 
Public interface iapplication {iconfigurationsource configsource {Get ;}} public class application: iapplication {private readonly iconfigurationsource configsource; Public Application (iconfigurationsource configsource) {This. configsource = configsource;} public iconfigurationsource configsource {get {return this. configsource ;}}}

 

Finally, applicationfactory Singleton creates an iapplication instance by using the createapplication method, returns the created instance to the caller, and keeps reference to the created instance, in this way, the iapplication instance currently executed can be obtained through applicationfactory Singleton anywhere in the framework.

Public class applicationfactory {Private Static readonly applicationfactory instance = new applicationfactory (); Private iapplication currentapplication; Private Static readonly object lockobj = new object (); Private applicationfactory () {} public static applicationfactory instance {get {return instance;} public static iapplication createapplication (iconfigurationsource source) {lock (lockobj) {If (instance. currentapplication = NULL) {lock (lockobj) {instance. currentapplication = new application (source) ;}} return instance. currentapplication ;}} public iapplication currentapplication {get {return currentapplication ;}}}

 

The above is the implementation of the various configuration methods supported in the framework. In other parts of the framework, you can use the following method to obtain the required configuration information:

 
// You can use the following method to create an iapplication instance during framework initialization. For example, you can use the main function of the console // application or Windows Forms application, or the global function of the Web application. execute this part of code in asax //: iconfigurationsource configfileconfigsource = new configfileconfigurationsource (); // use Web/app. config configuration file iapplication application = applicationfactory. createapplication (configfileconfigsource); // todo: execute other operations on the application // in the framework, use the following method to access the configuration data: frameworkconfigsection configsection = applicationfactory. instance. currentapplication. configsource. config; // todo: Use configsection to obtain configuration data

 

Of course, we can directly use the previously defined regularconfigurationsource without relying on the app/Web. config configuration file to provide configuration data to the framework by writing code. This method not only solves the framework testing problems mentioned above, but also provides strong type and smart sensing support for developers. For example:

Iconfigurationsource regularsource = new regularconfigurationsource (); regularsource. setapplication (typeof (application ). assemblyqualifiedname); regularsource. addobjectcontainer (typeof (unitycontainer ). assemblyqualifiedname); iapplication application = applicationfactory. createapplication (regularsource );

 

Configuration development mode for specific domain languages

Through the above analysis, it is not difficult to know that to implement a variety of configuration methods, whether it is based on the configuration file or other implementation methods, it is inseparable from the configuration section) and the development and maintenance of configuration data structures. For small application frameworks, you can usually write the configuration code manually. However, for medium and large application frameworks, to provide powerful development capabilities, configuration section and configuration data structure are often complicated. Manual maintenance of the configuration code is time-consuming and labor-consuming. To effectively maintain these configuration codes, we can use domain-specific language (DSL) to develop configuration sections and data structures, then, you can use various code generation methods to automate code generation. Therefore, when you need to change the configuration section or configure the data structure during the development of the application framework, developers do not need to deal with complicated code, but only need to modify the domain-specific language, the rest of the work can be handled directly by the automated code generation engine. When designing and developing a framework Configuration System, the advantage of using a domain-specific language is not only Automatic code generation, but also support the following two application scenarios:

    • While generating code, you can generate an XML schema for the configuration data structure, so that you can use intellisense technology when editing the configuration file using Visual Studio.
    • Add the CLR features related to the component model to the generated code, which greatly simplifies the development of the configuration file editor.

 

Open SourceCommunityThere is a configuration section design tool that basically meets the above two requirements. The official address of this tool is:Http://csd.codeplex.com. It supports multiple Visual Studio 2005, 2008, and 2010 versions at the same time. The support for Visual Studio 2010 is implemented in the form of extension packages. In my personal blog, there is an articleArticleThis tool is briefly introduced:Http://www.cnblogs.com/daxnet/archive/2011/09/16/2178377.htmlInterested readers can read the reference.

Another application of the domain-specific language in the application framework Configuration System is the fluent interface. According to the introduction in dsls in BOO domain specific ages in. net, domain-specific languages can be divided into four categories: External DSL, graphical DSL, internal/Embedded DSL, and fluent interface. Based on the above design, the application of fluent interface on the iconfigurationsource interface can bring a more intuitive programming experience to the users of the application framework. Now, let's extend the above iconfigurationsource to see How fluent interface facilitates programming. We use the extension methods method provided by. Net 3.0 to achieve this effect.

Public static class fluentinterfaceprovider {public static iconfigurationsource setapplication (this iconfigurationsource source, string provider) {source. config. application = new applicationelement {provider = provider}; return source;} public static iconfigurationsource addobjectcontainer (this iconfigurationsource source, string provider) {source. config. objectcontainers. add (New objectcontainerelement {provider = provider}); Return source ;}// you can: iconfigurationsource configsource = new regularconfigurationsource (); configsource. setapplication (typeof (application ). assemblyqualifiedname ). addobjectcontainer (typeof (unitycontainer ). assemblyqualifiedname );

 

I will not discuss much about the knowledge of specific languages in this field. I will introduce it in detail in other articles.

Summary

This section describes how to use a standalone testing tool to demonstrate the necessity of providing diverse configuration methods for the application framework. Then, a feasible design scheme is provided for this requirement, at the same time, this design is implemented in the form of pseudocode. At the end of the chapter, we also discussed the best practices for configuring system design and development in the application framework, that is, using domain-specific languages to maintain the configuration section and configure the data structure, in addition, in the process of using the framework, it is convenient to use domain-specific languages for development. To develop an application framework, you also need to design a reasonable and complete configuration data structure and configuration method for it. This chapter provides a solution for this. In practice, developers do not have to use this solution to implement the Configuration System of the framework. However, it serves as the best practice for the Configuration System Design and brings some reference value to developers.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.