Build Providers in Practice

Build providers are a powerful feature for empowering applications. ASP.NET AJAX uses build providers to create gateway classes that connect to external Web services.


December 05, 2006
URL:http://drdobbs.com/windows/build-providers-in-practice/196601783

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.

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.