I recently read asp. net2.0 advanced programming, saw the compiling method of the custom BuildProvider, encountered some problems during the learning period, and found that there are not many such issues on the network, I recorded what I learned and posted it here for future reference.
The build program is a component that can be inserted into the ASP. NET compilation system to provide custom compilation support for some file types. By parsing the source file content during compilation, the build provider can automatically generate a suitable proxy class. Build provides the program to generate compiled code and keep it synchronized with the source file. When the source file changes, the build provider executes the task again and updates everything.
For example, some buildproviders built in Asp.net, such:
System. Web. Compilation. PageBuildProvider
System. Web. Compilation. UserControlBuildProvider
System. Web. Compilation. MasterPageBuildProvider
....
To realize automatic conversion from database tables to classes, we need to write a custom OrmBuildProvider. configure OrmBuildProvider in config and add one in App_Code. after the map file is created, VS.net automatically converts the database table to a class:
<Compilation debug = "true">
<! -- Custom ORM provider -->
<BuildProviders>
<Add extension = ". map" type = "OrmBuildProviderSln. OrmBuildProvider"/>
</BuildProviders>
</Compilation>
Our goal is to develop a custom OrmBuildProvider to automatically convert database tables to classes and support both SQL Server and Oracle databases.
We are going to use an xml file to define the structure of the. map file:
<? Xml version = "1.0" encoding = "UTF-8"?>
<Mappings namespace = "OrmBuildProviderSln">
<! -- DatabaseType = "SQL | Oracle" -->
<Mapping databaseType = "SQL"
ConnectionString = "Data Source = localhost \ sqlexpress; Initial Catalog = MsPetShop4; Integrated Security = SSPI; Timeout = 5; Application Name = OrmBuildProviderWeb"
TableName = "Product"
ClassName = "Product"
SelectCommand = "SELECT [ProductId] as [ProductId]
, [CategoryId] as CategoryId
, [Name] as Name
, [Descn] as Descn
, [Image] as Image
FROM [MSPetShop4]. [dbo]. [Product]"
AllowPartialClass = "true"
AllowCollectionClass = "true"
CollectionClassName = "ProductCollection">
</Mapping>
<Mapping databaseType = "Oracle"
ConnectionString = "Data Source = ORA252; User Id = trig; Password = trig ;"
TableName = "Customer"
ClassName = "Customer"
SelectCommand = "select customercode, regielicenceno, customername, address, phone, booker, zhuanmailx, businessform, organizemode, jingyingfs, issuedate, disable, issuedept, region from tg_customer"
AllowPartialClass = "true"
AllowCollectionClass = "true"
CollectionClassName = "CustomerCollection">
</Mapping>
</Mappings>
We create a new class library project named OrmBuildProvider and add a new class OrmBuildProvider. This class must be integrated with BuildProvider and written into the GenerateCode method.
Public class OrmBuildProvider: BuildProvider
{
Public override void GenerateCode (AssemblyBuilder assemblyBuilder)
{
}
}
There are two ways to implement GenerateCode: CodeDOM API or TextWriter. CodeDOM requires a lot of APIS, but the generated code can be used in VB, C #; The TextWriter method is more direct, but can only generate code for a certain language. An example of using CodeDOM API is provided in aspnet 2.0 advanced programming. junqilian implements and extends the code using TextWriter, this enables OrmBuildProvider to support both SQL Server and Oracle databases.
Public override void GenerateCode (AssemblyBuilder assemblyBuilder)
{
//// // Using CodeDOM API
/// Slightly, the specific code can refer to the http://www.yuedu.cc/chapter/10482188/
//// // Using TextWriter. Only for C # by Du Changyu
TextWriter writer = assemblyBuilder. CreateCodeFile (this );
If (writer = null)
{
Return;
}
Try
{
String code = ParseFileAndCreateCode (base. VirtualPath );
Writer. Write (code );
}
Finally
{
Writer. Close ();
}
}
Private string ParseFileAndCreateCode (string fileName)
{
OrmDescriptor desc = ExtractInfo (fileName );
// OrmDescriptor desc = new OrmDescriptor ();
StringBuilder code = new StringBuilder ();
// Add some file header
Code. appendLine ("///////////////////////////////////// ////////////////////");
Code. AppendLine ("// This file generated automatically. do not change anything .");
Code. AppendLine ("// Because your change maybe dispeared when the application restarted .");
Code. AppendLine ("// Generated by OrmBuildProvider" + System. DateTime. Now. ToString ());
Code. AppendLine ("// Implemented by Duchangyu Co., changyudu@163.com ");
Code. appendLine ("///////////////////////////////////// ///////////////////");
Code. AppendLine ("using System. Collections. Generic ;");
Code. AppendLine ();
Code. AppendLine ("namespace" + desc. NameSpace );
Code. AppendLine ("{");
// Add a class present the table
For (int I = 0; I <desc. Descriptors. Length; I ++)
{
OrmTableDescriptor t = desc. Descriptors [I];
// Add a comment here
Code. AppendLine ("// ----------" + t. ClassName + "Class -----------");
Code. AppendLine ("public class" + t. ClassName );
Code. AppendLine ("{");
// Add the properties present the table columns
DataAdapter adapter = CreateDataAdapter (t. DatabaseType, t. SelectCommand, t. ConnectionString );
DataSet ds = new DataSet ();
Adapter. FillSchema (ds, SchemaType. Mapped );
DataTable dt = ds. Tables [0];
For (int j = 0; j <dt. Columns. Count; j ++)
{
DataColumn column = dt. Columns [j];
String colName = column. ColumnName;
Type colType = column. DataType;
String filedName = "_" + colName. ToLower ();
Code. AppendLine ("private" + colType. ToString () + "" + filedName + ";");
Code. AppendLine ("public" + colType. ToString () + "" + colName );
Code. AppendLine ("{");
Code. AppendLine ("set {" + filedName + "= value ;}");
Code. AppendLine ("get {return" + filedName + ";}");
Code. AppendLine ("}");
Code. AppendLine ();
}
Code. AppendLine ("}");
Code. AppendLine ();
// Add the collectionClass -- generic
If (t. AllowCollectionClass)
{
Code. AppendLine ("public class" + t. CollectionClassName + ": List <" + t. ClassName + "> ");
Code. AppendLine ("{}");
Code. AppendLine ();
}
}
Code. AppendLine ("}");
Return code. ToString ();
}
Public OrmDescriptor ExtractInfo (string fileName)
{
// Load the *. map document
XmlDocument doc = new XmlDocument ();
Using (Stream stream = VirtualPathProvider. OpenFile (fileName ))
{
Doc. Load (stream );
}
// Get the namespace information
XmlNode root = doc. DocumentElement;
String ns = root. Attributes ["namespace"]. Value;
// Visite the <mapping nodes>
XmlNodeList mappings = doc. SelectNodes ("Mappings/Mapping ");
OrmTableDescriptor [] descriptors = new OrmTableDescriptor [mappings. Count]; // allocate resource;
// List <OrmTableDescriptor> descriptors = new List <OrmTableDescriptor> (mappings. Count );
For (int I = 0; I <mappings. Count; I ++)
{
XmlNode mapping = mappings [I];
OrmTableDescriptor desc = new OrmTableDescriptor ();
Desc. ConnectionString = mapping. Attributes ["connectionString"]. Value;
Desc. TableName = mapping. Attributes ["tableName"]. Value;
Desc. ClassName = mapping. Attributes ["className"]. Value;
Desc. SelectCommand = mapping. Attributes ["selectCommand"]. Value;
Bool allowPartialClass;
Bool. TryParse (mapping. Attributes ["allowPartialClass"]. Value, out allowPartialClass );
Desc. AllowPartialClass = allowPartialClass;
Bool allowCollection = false;
Boolean. TryParse (mapping. Attributes ["allowCollectionClass"]. Value, out allowCollection );
Desc. AllowCollectionClass = allowCollection;
If (allowCollection)
{
Desc. CollectionClassName = mapping. Attributes ["collectionClassName"]. Value;
}
Desc. DatabaseType = mapping. Attributes ["databaseType"]. Value;
Descriptors [I] = desc;
// Descriptors. Add (desc );
}
// Pack all info and return
OrmDescriptor ormDescriptor = new OrmDescriptor ();
OrmDescriptor. NameSpace = ns;
OrmDescriptor. Descriptors = descriptors;
Return ormDescriptor;
}
The OrmDescriptor and OrmTableDescriptor classes are introduced here.
Using System;
Using System. Collections. Generic;
Using System. Text;
Namespace OrmBuildProviderSln
{
Public class OrmDescriptor
{
Private string nameSpace;
Public string NameSpace
{
Get {return nameSpace ;}
Set {nameSpace = value ;}
}
Private OrmTableDescriptor [] descriptors;
Public OrmTableDescriptor [] Descriptors
{
Get {return descriptors ;}
Set {descriptors = value ;}
}
// Private List <OrmTableDescriptor> descriptors;
// Internal List <OrmTableDescriptor> Descriptors
//{
// Get {return descriptors ;}
// Set {descriptors = value ;}
//}
}
}
Using System;
Using System. Collections. Generic;
Using System. Text;
Namespace OrmBuildProviderSln
{
Public class OrmTableDescriptor
{
Private string connectionString;
Public string ConnectionString
{
Get {return connectionString ;}
Set {connectionString = value ;}
}
Private string tableName;
Public string TableName
{
Get {return tableName ;}
Set {tableName = value ;}
}
Private string className;
Public string ClassName
{
Get {return className ;}
Set {className = value ;}
}
Private string selectCommand;
Public string SelectCommand
{
Get {return selectCommand ;}
Set {selectCommand = value ;}
}
Private bool allowPartialClass;
Public bool AllowPartialClass
{
Get {return allowPartialClass ;}
Set {allowPartialClass = value ;}
}
Private bool allowCollectionClass;
Public bool AllowCollectionClass
{
Get {return allowCollectionClass ;}
Set {allowCollectionClass = value ;}
}
Private string collectionClassName;
Public string CollectionClassName
{
Get {return collectionClassName ;}
Set {collectionClassName = value ;}
}
Private string databaseType;
Public string DatabaseType
{
Get {return databaseType ;}
Set {databaseType = value ;}
}
}
}
In this way, when we add the orm. map File to App_Code, Asp.net will monitor and automatically generate a corresponding class under the temporary directory of aspnet.
C: \ WINDOWS \ Microsoft. NET \ Framework \ v2.0.50727 \ Temporary ASP. NET Files \ ormbuildproviderweb \ be94c8e8 \ 9adb5db3 \ Sources_App_Code \ orm. map.72cecc2a. cs
//////////////////////////////////////// /////////////////
// This file generated automatically. do not change anything.
// Because your change maybe dispeared when the application restarted.
// Generated by OrmBuildProvider 2008-02-29 21:25:03
// Implemented by Duchangyu changyudu@163.com
//////////////////////////////////////// ////////////////
Using System. Collections. Generic;
Namespace OrmBuildProviderSln
{
// ---------- Product Class -----------
Public class Product
{
Private System. String _ productid;
Public System. String ProductId
{
Set {_ productid = value ;}
Get {return _ productid ;}
}
Private System. String _ categoryid;
Public System. String CategoryId
{
Set {_ categoryid = value ;}
Get {return _ categoryid ;}
}
Private System. String _ name;
Public System. String Name
{
Set {_ name = value ;}
Get {return _ name ;}
}
Private System. String _ descn;
Public System. String Descn
{
Set {_ descn = value ;}
Get {return _ descn ;}
}
Private System. String _ image;
Public System. String Image
{
Set {_ image = value ;}
Get {return _ image ;}
}
}
Public class ProductCollection: List <Product>
{}
// ---------- Category Class -----------
Public class Category
{
Private System. String _ categoryid;
Public System. String CategoryId
{
Set {_ categoryid = value ;}
Get {return _ categoryid ;}
}
Private System. String _ name;
Public System. String Name
{
Set {_ name = value ;}
Get {return _ name ;}
}
Private System. String _ descn;
Public System. String Descn
{
Set {_ descn = value ;}
Get {return _ descn ;}
}
}
Public class CategoryCollection: List <Category>
{}
// ---------- Customer Class -----------
Public class Customer
{
Private System. String _ customercode;
Public System. String CUSTOMERCODE
{
Set {_ customercode = value ;}
Get {return _ customercode ;}
}
Private System. String _ regielicenceno;
Public System. String REGIELICENCENO
{
Set {_ regielicenceno = value ;}
Get {return _ regielicenceno ;}
}
Private System. String _ customername;
Public System. String CUSTOMERNAME
{
Set {_ customername = value ;}
Get {return _ customername ;}
}
Private System. String _ address;
Public System. String ADDRESS
{
Set {_ address = value ;}
Get {return _ address ;}
}
Private System. String _ phone;
Public System. String PHONE
{
Set {_ phone = value ;}
Get {return _ phone ;}
}
Private System. String _ booker;
Public System. String BOOKER
{
Set {_ booker = value ;}
Get {return _ booker ;}
}
Private System. String _ zhuanmailx;
Public System. String ZHUANMAILX
{
Set {_ zhuanmailx = value ;}
Get {return _ zhuanmailx ;}
}
Private System. String _ businessform;
Public System. String BUSINESSFORM
{
Set {_ businessform = value ;}
Get {return _ businessform ;}
}
Private System. String _ organizemode;
Public System. String ORGANIZEMODE
{
Set {_ organizemode = value ;}
Get {return _ organizemode ;}
}
Private System. String _ jingyingfs;
Public System. String JINGYINGFS
{
Set {_ jingyingfs = value ;}
Get {return _ jingyingfs ;}
}
Private System. DateTime _ issuedate;
Public System. DateTime ISSUEDATE
{
Set {_ issuedate = value ;}
Get {return _ issuedate ;}
}
Private System. String _ disable;
Public System. String DISABLE
{
Set {_ disable = value ;}
Get {return _ disable ;}
}
Private System. String _ issuedept;
Public System. String ISSUEDEPT
{
Set {_ issuedept = value ;}
Get {return _ issuedept ;}
}
Private System. String _ specdelivery;
Public System. String SPECDELIVERY
{
Set {_ specdelivery = value ;}
Get {return _ specdelivery ;}
}
}
Public class CustomerCollection: List <Customer>
{}
}
In this way, we can use the smart notification feature in VS2005 in web projects to conveniently use the Product, Category, and Customer categories corresponding to database tables,
Protected void Page_Load (object sender, EventArgs e)
{
OrmBuildProviderSln. Product p = new OrmBuildProviderSln. Product ();
P. Name = "product Name ";
P. ProductId = "2 ";
OrmBuildProviderSln. ProductCollection pc = new OrmBuildProviderSln. ProductCollection ();
Pc. Add (p );
}
The following describes how to debug BuildProvider.
When developing a custom BuildProvide, you need to write a lot of code, which will inevitably lead to errors. How can I solve the debugging problem? We set up a breakpoint on a method. The code execution is automatically called by aspnet, And the breakpoint cannot be captured by the root node in vs2005. Later in the Internet to find someone at the same time open two VS2005, one for triggering, one for debugging, huh, this is new, but too cumbersome http://devtalk.dk/2007/10/27/Expand+And+Debug+A+Custom+BuildProvider.aspx
Later I thought about why I had to go through the debugging of the BuildProvider class library. I built a web site program and made a page at will, it's okay to use BuildProvider as a common class for debugging. below is how to debug the ExtractInfo () method. You can track the OrmBuildProvider class by setting breakpoints in the last sentence. Of course, for convenience, copy orm. map into An orm. xml file and place it outside App_Code. This makes debugging easier.
Protected void button#click (object sender, EventArgs e)
{
OrmBuildProviderSln. OrmBuildProvider ormBuild = new OrmBuildProviderSln. OrmBuildProvider ();
String path = Request. ApplicationPath + "/orm. xml ";
OrmBuild. ExtractInfo (path );
}
Well, it's not easy to write so much on the first technical blog. I will study more correctly and record more in the future. I just started my research and asked everyone to criticize and correct anything wrong.