/*
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( ));
}
}