Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Build Providers in Practice


In ASP.NET 2.0, commonly used file types are associated with a special component-the build provider. The build provider is charged with the task of providing a code representation of the contents of the specified file. Put another way, the build provider understands the contents of the file and produces a compilable class that is added to the application domain. In-box build providers exist for all resources that were dynamically compiled in ASP.NET 1.x; e.g., ASPX, ASMX, ASCX, ASHX, ASAX, plus a number of new ones.

For example, in ASP.NET 2.0 a build provider creates a Web service proxy class from a WSDL file. But there's more. Typed DataSets are created dynamically from an XSD file; themes are created from a subtree of files that contains stylesheets and skins; required satellite assemblies for localized resources are now created dynamically from .resx files. A build provider generates compilable code and keeps it in sync with the source file; as the source file changes, the build provider kicks in again and updates everything.

The ASP.NET root web.config file contains a number of bindings between file types and native build providers. Bindings are stored in a new section under the < compilation> section. The new section is named <buildProviders> and takes the following form:


<compilation>
  <buildProviders>
    <add extension=".aspx" 
        type="System.Web.Compilation.PageBuildProvider" />
    <add extension=".ascx" 
        type="System.Web.Compilation.UserControlBuildProvider" />
    <add extension=".master" 
        type="System.Web.Compilation.MasterPageBuildProvider" />
     <add extension=".asmx" 
        type="System.Web.Compilation.WebServiceBuildProvider" />
     <add extension=".ashx" 
        type="System.Web.Compilation.WebHandlerBuildProvider" />
     <add extension=".resx" 
        type="System.Web.Compilation.ResXBuildProvider" />
     <add extension=".resources" 
        type="System.Web.Compilation.ResourcesBuildProvider" />
     <add extension=".wsdl" 
        type="System.Web.Compilation.WsdlBuildProvider" />
     <add extension=".xsd" 
        type="System.Web.Compilation.XsdBuildProvider" />
     <add extension=".js" 
        type="System.Web.Compilation.ForceCopyBuildProvider" />
   </buildProviders>
 </compilation>
 

All these build providers are internal classes defined in the System.Web.Compilation namespace inside the system.web assembly. All these classes derive from one common root--the BuildProvider class.

An important change in ASP.NET 2.0 that somewhat relates to the introduction of build providers is that you can no longer have class files scattered in the project folders. All class source files you need must go in the App_Code folder. In ASP.NET 1.x, instead, these files could be located anywhere in the project. The only class files allowed outside the App_Code folder are code-behind classes of Web pages. Needless to say, you can always add class files to a class library project and link the resulting assembly to the ASP.NET application.

The list of native build providers can be extended at will to incorporate custom build providers for custom file types. All that you have to do is create a new build provider class--that is, a class that inherits from System.Web.Compilation.BuildProvider. At the very minimum, the derived class overrides the method GenerateCode. Here's the typical outline of the sample class:


public class MyBuildProvider : BuildProvider
{
   public MyBuildProvider() {
   }

   public override void GenerateCode(AssemblyBuilder ab) 
   {
      // Get the virtual path to the source file
      string fileName = base.VirtualPath;

      // Get the tree representing the generated code
      CodeCompileUnit code = BuildCodeTree(fileName);

	// Build an assembly using the code tree
	ab.AddCodeCompileUnit(this, code);
   }
}

As the name suggests, the method GenerateCode parses the input file and generates one or more source classes with the collected information. When done, the code is passed to the assembly builder class and compiled. Nicely enough, the AssemblyBuilder class not just compiles the class to an assembly but also causes the dynamically created assembly to be loaded in the current application domain.

The BuildCodeTree method is the core of the build provider. It is responsible for exposing the code to compile as a CodeDOM tree. Internally, BuildCodeTree processes the input file and generates a CodeDOM tree-that is an object graph that describes a piece of code (type definition, members, methods, statements) in a language-agnostic manner.

When it comes to generating a class based on the contents of an input file, CodeDOM is not the only possible option. You can also construct the final source code to be compiled by concatenating strings in a text writer object. In this case, GenerateCode takes a slightly different form:


public override void GenerateCode(AssemblyBuilder ab)
{
    TextWriter tw = ab.CreateCodeFile(this);
    if (tw == null)
       return;

    // Parse the file and generate the code we want from it
    string code = ParseFileAndCreateCode(base.VirtualPath);
    tw.Write(code);
    tw.Close();
}

Any changes made to the source of a dynamically compiled file automatically invalidates the corresponding assembly, which will then be recreated. You probably won't write custom build providers every week; but still build providers represent a powerful feature for empowering applications. As an example, consider that ASP.NET AJAX--the new version of ASP.NET that incorporates AJAX features--uses build providers to create gateway classes that connect to external Web services.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.