/* This file accesses a custom config section in the web configuration file to construct a list of JavaScript files that will be included in this page. After the list is created, this page then combines the contents of each of the pages into a StringBuilder and then flushes that StringBuilder to the screen. ==Custom Config Sections== As .NET Web Developers we know all about keeping our code separate. If you are an object-oriented guru, then you know even more so the important of high cohesion. If you want to be consistence, then keep your JavaScript files highly cohesive. To avoid a client web browser having to request 20-30 JavaScript files, you can use just consolidate the files into one major file and then stream that JavaScript file to the client. This will not only allow you to have a centralized file the clients have to download, but it also gives you more centralized control of your JavaScript caching. Furthermore, since it's just one file, if you want to minimize the JavaScript file or obfuscate it, you can simply do so in one centralized virtual file without having to maintain both an obfuscated and a readable JavaScript file.With this method, any processing you do on your files will be purely virtual and can be completely cached. This is exactly what Gmail is doing with their JavaScript system. You think they really work with one 500kJavaScript file for all their JavaScript development? One finally note: if you are doing this, make sure you are a web development professional who knows what you are doing. Let me rephrase: if you are doing this, make sure you are following the ECMAScript specification as missing any piece of syntax will break all your files. That's OK though... would you really expect that to compile in C#? Of course not. Professional programmers follow the specifications and read the manual, not just throw things together and learn programming by watching compiler messages and seeing "what works". Your "years" of "experience" don't mean a thing if you aren't doing your work properly, so make sure you are doing your work according to the specifications. Creating a custom config section is very powerful technique for extending the configuration capabilities of your application. For example, the custom config section used in this application allows for the following XML branch to be places in the web.config file: Everything about this configuration is completely custom and can very well be more or less complex depending on the requirements of the application. To create a custom config section, *ALWAYS* write the XML block that you want to use *FIRST*. Never create any implementation without knowing the interface of something. Some people call this the "Sellsian" approach, but I simply call it common sense. Whenever you create any implementation of anything be it a type, a method, a property, an event, a service, an XML block, or whatever else, you need to always write the code you will use to consume the resource. So, if you are creating a framework, first think about how you want to use it, then make that usage a reality. This XML block in the web.config file is no except. As a side note, sometimes I refer to the logical progression of this line of thought as the "Onion Method". I call it that because you design from the inside out, *STARTING* with the purpose (i.e. what is the purpose of this thing to begin with?), then moving to UI design (i.e. how will this be used?), then to the logic layer design (i.e. how will THIS be used?), and then to the internal design of the rest of the framework (i.e. how will THIS be used? -- do see the pattern yet?) Then you BUILD from the inside out. The only exception to this is the database model as that is somewhat parallel to this entire process and relates more to the result of the application than does anything else, but proper database design is a completely different topic. Also, as with any onion, the goal is to be done as efficiently as possible, with out coming to the point of crying along the way. Ergo, the "Onion Method". After deciding on how you want to use the custom config section, you need to connect it to the mechanics of your custom config section (discussed in a minute). The following XML is an example of how to do this connection.
Please note that the configSections portion of the web.config must be the first child under the configuration element. Also note that the 'section' element has an attribute called 'name' which matches a branch in our XML that uses our custom config section. Not only that, but also note that the 'name' attribute of the 'sectionGroup' element matches the parent element in our XML. Using section groups is optional, but it definitely helps help keep your custom config sections together. Now that you are successfully using the yet to be created custom config section, it's time to make the theoretical idea of it a reality. Creating a custom config section is actually rather simple as the entire concept is based around the idea of the IConfigurationSectionHandler interface. So, to start off with, you need to create a class that implements this interface. This interface requires you to implement one simple method: Object Create(Object parent, Object configContext, XmlNode section); Looking at our web.config file we see that we our custom config section implementation is in "SampleLibrary.Configuration.JavaScriptImportSectionHandler, SampleLibrary", which means that our implementation is in the 'JavaScriptImportSectionHandler' class of the 'SampleLibrary.Configuration' namespacein the 'SampleLibrary' assembly. Now making the implementation a reality is actually rather a joke. The 'section' parameter of the Create method we are required to create is of type 'XmlNode' and contains the 'JavaScriptImport' XML element we created in our web.config file. With this in mind, we simply need to iterate through the XML children and save the data in some structure that we can use in our application. Typically when I do this I'll create a simple class to hold the data, 'JavaScriptImport' in this case, and a collection class suffixed with "ConfigSection", in this case 'JavaScriptImportConfigSection' that does nothing more than act as a strongly typed collection for the data storage class ('JavaScriptImport'). With this implementation, you simply access the configuration section by calling the 'GetSection" method of the 'ConfigurationManager' class. This file contains all the implementation for the above theory I just described. I didn't paste snippet samples through-out my explanation as that would destroy possible learning. Forcing you to go and look at the code I'm talking about will greatly help you learn this topic. As a final note, I said that you should use something before you create it, but what about the following line? JavaScriptImportConfigSection cs = (JavaScriptImportConfigSection)ConfigurationManager.GetSection("SampleSolution/JavaScriptImport"); This is the line in the Includes.js.aspx.cs file that retrieves our custom configuration section. We didn't write this first, why? You only need to use something before you create, if you are actually creating it. In this case, you are using a pre-created API and therefore the design is already done for you and the implementation of it can, and should, be delayed until a later time (i.e. when you actually want to use the custom config section). */ using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using System.IO; using System.Text; using SampleLibrary.Configuration; public partial class Code_includes : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { String[] files = Directory.GetFiles(Server.MapPath("."), "*.js"); JavaScriptImportConfigSection cs = (JavaScriptImportConfigSection)ConfigurationManager.GetSection("SampleSolution/JavaScriptImport"); if (cs == null) { Response.Write("Null"); return; } StringBuilder javascript = new StringBuilder( ); foreach (JavaScriptImport import in cs.JavaScriptImportSet) { javascript.AppendLine(File.ReadAllText(Server.MapPath(import.Name))); javascript.AppendLine( ); } Response.Write(javascript.ToString( )); } }