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:
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.
private CodeTypeDeclaration CreateClass(string line)
{
string[] values = line.Split(new string[1] { "|" },
StringSplitOptions.RemoveEmptyEntries);
string className = values[0];
CodeTypeDeclaration cls = new CodeTypeDeclaration(className);
string fieldName = values[1];
CodeMemberField abbrField = CreateField(fieldName);
cls.Members.Add(abbrField);
string[] zipCodes = values[2].Split(new string[1] { ";" },
StringSplitOptions.RemoveEmptyEntries);
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;
}
private CodeMemberField CreateField(string fieldName)
{
CodeMemberField field = new CodeMemberField(typeof(String),
string.Format("_{0}", fieldName));
field.Attributes = MemberAttributes.Private | MemberAttributes.Static;
field.InitExpression =
new CodeSnippetExpression(string.Format("\"{0}\"", fieldName));
return field;
}
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;
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: