Asp.net 编译原理 之二: 提供程序
从asp.net 1.0中,如果用户定义了一个新的组件(cs文件),需要预编译组件后application才可以使用。在Asp.net 2.0开始改变了这种使用方式。asp.net 2.0将website的结构进行重组,新添了一些默认的文件夹,这些文件夹用于不同的场合。可以通过“Add Asp.net Folder”添加可用的特殊文件夹。
其中有个App_Code的文件夹,IDE可以自动编译此文件夹内的文件,即:当用户往此文件夹添加或拖入一个新文件时,IDE会自动编译,Application的其它地方可以直接使用编译后的类,并享受VS的IntellSense功能。.net的这个特性是由BuildProvider支持的。BuildProvider提供程序是一个可以插入ASP.NET编译系统,为某些文件类型提供自定义编译支持的组件。通过在编译时解析源文件内容,build提供程序可以自动生成一个合适的源代码代理类。build提供程序生成可编译的代码,并使它与源文件保持同步。当源文件发生变化时,build提供程序再次执行,并更新一切。
内置的提供程序
在Asp.net系统级web.config文件中定义了系统的内置提供程序。
<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=".soap" type="System.Web.Compilation.WebServiceBuildProvider"/>
<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"/>
<add extension=".lic" type="System.Web.Compilation.IgnoreFileBuildProvider"/>
<add extension=".licx" type="System.Web.Compilation.IgnoreFileBuildProvider"/>
<add extension=".exclude" type="System.Web.Compilation.IgnoreFileBuildProvider"/>
<add extension=".refresh" type="System.Web.Compilation.IgnoreFileBuildProvider"/>
<add extension=".xoml" type="System.ServiceModel.Activation.WorkflowServiceBuildProvider, System.WorkflowServices, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add extension=".svc" type="System.ServiceModel.Activation.ServiceBuildProvider, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</buildProviders>
这些提供程序只是实现了System.Web.Compilation.BuildPrivder抽象类的类。BuildProvider包含一个重要的函数:
public virtual void GenerateCode(AssemblyBuilder assemblyBuilder) { } |
这个函数用于生成虚拟目录下特定文件类型的源代码,并把生成的源代码添加到指定的程序集生成器(AssemblyBuilder)中。方法内需要使用System.CodeDom命名空间下的类来构建源代码。该命名空间包含可用于生成源代码文档的元素和结构的类,可用来建立源代码文档结构的模型。
定制BuildProvider
分三步完成定制过程:
1 定义要编译的文件结构
2 BuildProvider根据文件结构生成源代码类
3 使用定制的BuildProvider
1 定义文件结构
假设要根据定制.car文件中的设置动态构建一个Car类,以便使该类能够在项目中被多次利用。定做一.car文件类型的结构如下:
<?xml version="1.0" encoding="utf-8" ?>
<car name="">
<color/>
<door/>
<speed/>
</car>
BuildProvider将根据xml文件的信息创建一个类文件。
2 定制BuildProvider
using System.IO;
using System.Web.Compilation;
using System.Xml;
using System.CodeDom;
namespace MyBuildProvider
{
public class CarBuildProvider : BuildProvider
{
public CarBuildProvider ()
{
}
public override void GenerateCode(AssemblyBuilder assemblyBuilder)
{
XmlDocument carXmlDoc = new XmlDocument();
using (Stream passedFile = this.OpenStream())
{
carXmlDoc.Load(passedFile);
}
XmlNode mainNode = carXmlDoc.SelectSingleNode("/car");
string selectionMainNode = mainNode.Attributes["name"].Value;
XmlNode colorNode = carXmlDoc.SelectSingleNode("/car/color");
string selectionColorNode = colorNode.InnerText;
XmlNode doorNode = carXmlDoc.SelectSingleNode("/car/door");
string selectionDoorNode = doorNode.InnerText;
XmlNode speedNode = carXmlDoc.SelectSingleNode("/car/speed");
string selectionSpeedNode = speedNode.InnerText;
CodeCompileUnit ccu = new CodeCompileUnit();
CodeNamespace cn = new CodeNamespace();
CodeMemberProperty cmp1 = new CodeMemberProperty();
CodeMemberProperty cmp2 = new CodeMemberProperty();
CodeMemberMethod cmm1 = new CodeMemberMethod();
cn.Imports.Add(new CodeNamespaceImport("System"));
cmp1.Name = "Color";
cmp1.Type = new CodeTypeReference(typeof(string));
cmp1.Attributes = MemberAttributes.Public;
cmp1.GetStatements.Add(new CodeSnippetExpression("return \"" + selectionColorNode + "\""));
cmp2.Name = "Door";
cmp2.Type = new CodeTypeReference(typeof(int));
cmp2.Attributes = MemberAttributes.Public;
cmp2.GetStatements.Add(new CodeSnippetExpression("return " + selectionDoorNode));
cmm1.Name = "Go";
cmm1.ReturnType = new CodeTypeReference(typeof(int));
cmm1.Attributes = MemberAttributes.Public;
cmm1.Statements.Add(new CodeSnippetExpression("return " + selectionSpeedNode));
CodeTypeDeclaration ctd = new CodeTypeDeclaration(selectionMainNode);
ctd.Members.Add(cmp1);
ctd.Members.Add(cmp2);
ctd.Members.Add(cmm1);
cn.Types.Add(ctd);
ccu.Namespaces.Add(cn);
assemblyBuilder.AddCodeCompileUnit(this, ccu);
}
}
}
CarBuildProvider读取文件的元素及属性,利用System.CodeDom命名空间下的类创建源代码类。
要使定制的BuildProvider起作用,就需要在web.config配置段中注册:
<compilation debug="true">
<buildProviders>
<add extension=".car" type="MyBuildProvider.Car"/>
</buildProviders>
</compilation>
</system.web>
3 使用定制的BuildProvider
定义一个.car示例文件如下:
<?xml version="1.0" encoding="utf-8" ?>
<car name="EvjenCar">
<color>Red</color>
<door>4</door>
<speed>150</speed>
</car>
将car示例文件添加到App_Code文件夹下,IDE会自动调用MyBuildProvider为文件生成源代码类。示例文件生成的类代码如下:
public class EvjenCar {
public virtual string Color {
get {
return "Red";
}
}
public virtual int Door {
get {
return 4;
}
}
public virtual int Go() {
return 150;
}
}
在Application的其它地方就可以访问这个类,并使用VS的智能提示。