Click here to Skip to main content
Click here to Skip to main content
Go to top

Extending ASP.NET 2.0 Part I: Creating Custom Build Providers

, 6 Nov 2005
Rate this:
Please Sign up or sign in to vote.
Extend the ASP.NET runtime by creating custom build providers for custom project resources.

Sample Image - main.jpg

Introduction

ASP.NET is a great managed environment that supports dynamic compilation for its registered resources. The aim of this article is to show how one can take advantage of dynamic compilation through the use of custom build providers.

The BuildProvider Class

The System.Web.Compilation.BuildProvider abstract class provides a set of methods and properties that allows ASP.NET to generate source code. To use this class, one must implement it and add an element under the system.web/compilation/buildProviders section under the web.config (or machine.config for your web server).

This list outlines how the ASP.NET build environment uses your instance of BuildProvider:

  • Build environment looks at the registered build providers within web.config.
  • An instance of your provider is created when the environment encounters a file extension within the App_Code special folder that matches the extension attribute of your configuration entry.
  • An AssemblyBuilder object is passed into your builder's GenerateCode method.
  • Your provider uses the AssemblyBuilder and other System.Web.Compilation classes to add source code for your file into the overall assembly.

To find out more about the BuildProvider class, please see the References section.

A ZipCode BuildProvider

This article uses a simple build provider that parses a file containing zip code information. The provider creates a state class for each line in the file and exposes the zip codes as properties. The custom file and the generated class look like this:

Contents of file All.zipcode:

Iowa|IA|50311;51442;50111;50021;50023
Nebraska|NE|12345;67890
Texas|TX|99999;12345

Generated state class:

A custom zip class

As previously stated, the provider must be registered within web.config in order for the runtime to hook up your provider. The entry for the ZipCodeBuildProvider looks like:

<buildProviders>
    <add appliesTo="Code" 
        extension=".zipCode" 
        type="Lozanotek.Examples.ZipCodeBuildProvider, BuildProvider"/>
</buildProviders>

In order to generate source code that will be compiled into an assembly, you must make use of the classes under the System.Web.Compilation namespace to create an abstract syntax tree (AST) to give to the runtime through the AssemblyBuilder class. The CreateClass, CreateField and CreateProperty methods are used to create a custom state class by parsing a line in the file and creating their corresponding compilation units.

/// <span class="code-SummaryComment"><summary>
</span>
/// Creates a class by parsing the given line
/// <span class="code-SummaryComment"></summary>
</span>
/// <span class="code-SummaryComment"><param name="line">Line to parse</param>
</span>
/// <span class="code-SummaryComment"><returns>A class declaration for a custom state class</returns>
</span>
private CodeTypeDeclaration CreateClass(string line)
{
    // Parse out the tokens
    string[] values = line.Split(new string[1] { "|" },          
                            StringSplitOptions.RemoveEmptyEntries);

    // Set the name of the class
    string className = values[0];
    CodeTypeDeclaration cls = new CodeTypeDeclaration(className);

    // Set an internal field and its value
    string fieldName = values[1];
    CodeMemberField abbrField = CreateField(fieldName);
    cls.Members.Add(abbrField);

    // Get the zip codes listed for the states
    string[] zipCodes = values[2].Split(new string[1] { ";" }, 
                               StringSplitOptions.RemoveEmptyEntries);

    // Create a property for each of the zip codes found
    foreach (string zipCode in zipCodes)
    {
        string tempZip = zipCode.Trim();
        CodeMemberField zipField = CreateField(tempZip);
        CodeMemberProperty zipProperty = CreateProperty(tempZip);

        cls.Members.Add(zipField);
        cls.Members.Add(zipProperty);
     }

     return cls;
}

/// <span class="code-SummaryComment"><summary>
</span>
/// Creates a private string field to store the value for the zip code
/// <span class="code-SummaryComment"></summary>
</span>
/// <span class="code-SummaryComment"><param name="fieldName">Name of the fild to create</param>
</span>
/// <span class="code-SummaryComment"><returns>A unit representing an private string field</returns>
</span>
private CodeMemberField CreateField(string fieldName)
{
    // Specify that you want to create a string field with the given name
    CodeMemberField field = new CodeMemberField(typeof(String), 
                                  string.Format("_{0}", fieldName));

    // Make the field private and static
    field.Attributes = MemberAttributes.Private | MemberAttributes.Static;

    // Assign the value of the field to the zip code
    field.InitExpression = 
      new CodeSnippetExpression(string.Format("\"{0}\"", fieldName));

    return field;
}

/// <span class="code-SummaryComment"><summary>
</span>
/// Creates a property that returns the value of a string field
/// <span class="code-SummaryComment"></summary>
</span>
/// <span class="code-SummaryComment"><param name="propertyName">Name of the property to create</param>
</span>
/// <span class="code-SummaryComment"><returns>A unit representing a public static property</returns>
</span>
private CodeMemberProperty CreateProperty(string propertyName)
{
    CodeMemberProperty prop = new CodeMemberProperty();
    prop.Name = string.Format("zip{0}", propertyName);
    prop.Type = new CodeTypeReference(typeof(String));

    prop.Attributes = MemberAttributes.Public | MemberAttributes.Static;

    // Build the body of the property by assigning it a get statement
    prop.GetStatements.Add(new CodeMethodReturnStatement(
                    new CodeFieldReferenceExpression(null,
                    string.Format("_{0}", propertyName))));

    return prop;
}

Conclusion

I hope this article has shown you, the developer, how to take advantage of the extensibility of the ASP.NET runtime. In my next Extending ASP.NET 2.0 article, I will demonstrate how to extend ASP.NET even further by creating custom expression builders.

Please feel free to make any comments or suggestions to this article by using the forums below.

References

During the writing of this article, I used the following resources:

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Javier Lozano
Web Developer
United States United States
Javier is a consultant specializing in ASP.NET, system architecture, and training. He's also a MCSD .NET and a co-founder of the Iowa DotNET Users Group. Recently, Javier was awarded the Visual Developer ASP.NET MVP award from Microsoft.
 
On his blog you can find posts on ASP.NET, architecture, design patterns, XML and just recently, Indigo. Javier enjoys giving back to the community by speaking at user groups, local/regional .NET events, being active in forums and by writing articles for Code Project.
 
In his spare time, Javier enjoys spending time with his wife, newborn son, two golden retrievers and writing about himself in third person.
 
Click here to read Javier's blog.

Comments and Discussions

 
QuestionCan the build provider itself be dynamically compiled Pinmemberdarin the developer8-Jan-06 5:06 
AnswerRe: Can the build provider itself be dynamically compiled PinmemberJavier Lozano21-Jan-06 10:35 
AnswerRe: Can the build provider itself be dynamically compiled PinmemberJavier Lozano22-Jan-06 10:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140926.1 | Last Updated 7 Nov 2005
Article Copyright 2005 by Javier Lozano
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid