• 自己编写BuildProvider来实现ORM以及BuildProvider的调试


       最近在看《asp.net2.0高级编程》,看到了自定义BuildProvider的编写方法,学习期间也遇到了一些问题,发现网络上这方面的东西还不多,我把我学习过程中的东西记录下来,贴在这里备查。
       build提供程序是一个可以插入ASP.NET编译系统,为某些文件类型提供自定义编译支持的组件。通过在编译时解析源文件内容,build提供程序可以自动生成一个合适的代理类。build提供程序生成可编译的代码,并使它与源文件保持同步。当源文件发生变化时,build提供程序再次执行,并更新一切。

          比如Asp.net内置的一些BuildProvider,如:
    System.Web.Compilation.PageBuildProvider
    System.Web.Compilation.UserControlBuildProvider
    System.Web.Compilation.MasterPageBuildProvider
    ....

       这里为了实现数据库表到类的自动转换,需要编写一个自定义的OrmBuildProvider,我们在web.config中配置好OrmBuildProvider,在App_Code中增加一个.map文件后,VS.net会自动帮我们把数据库表转换成类:
    <compilation debug="true">
                <!--自定义的ORM provider-->
                <buildProviders>
                    <add extension=".map" type="OrmBuildProviderSln.OrmBuildProvider"/>
                </buildProviders>
    </compilation>

    我们的目标,开发一个自定义的OrmBuildProvider,实现数据库表到类的自动转换,并且要同时支持SQL Server和Oracle数据库。

    我们准备用一个xml文件来定义.map文件的结构:
    <?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, specdelivery from tg_customer"
           allowPartialClass="true"
           allowCollectionClass="true"
           collectionClassName="CustomerCollection">
        </Mapping>
    </Mappings>

    我们新建一个类库项目,命名为OrmBuildProvider,新添加一个类OrmBuildProvider,这个类需要集成BuildProvider,并冲写GenerateCode方法。

    public class OrmBuildProvider : BuildProvider
       {

         public override void GenerateCode(AssemblyBuilder assemblyBuilder)
         {
         }

    }

    实现GenerateCode有两种方法可以选择,CodeDOM API或者TextWriter直接输出。使用CodeDOM需要了解一大堆的API,但生成的代码可以适用于VB,C#;使用TextWriter的方法比较直接,但只能生成针对某种语言的代码。《aspnet 2.0高级编程》上给出了使用CodeDOM API的例子,峻祁连实现了使用TextWriter的代码,并进行了扩展,使得OrmBuildProvider同时支持Sql Server和Oracle两种数据库。

      public override void GenerateCode(AssemblyBuilder assemblyBuilder)
    {
         ///////////Using CodeDOM API
         //// 略,具体代码可以参考http://www.yuedu.cc/chapter/10482188/

         //////////////////////////Using TextWriter.Only for C#   by 杜长宇
         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     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;

    }

    这里引入了类OrmDescriptor和OrmTableDescriptor

    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; }
            }
        }
    }

    这样,当我们在App_Code中加入orm.map文件时,Asp.net就会监测到,自动在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>
      { }

    }

    这样我们在web项目中就可以利用VS2005中的智能提示功能,很方便的使用和数据库表对应的类Product,Category和Customer等,

    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);
    }

    下面说说BuildProvider的调试方法。

    开发自定义的BuildProvide时,需要编写大量的代码,这就难免出错。调试的问题怎么解决么?我们在某个方法上设立断点,可代码的执行是aspnet自动调用的,vs2005中根本就不能捕获到这个断点。后来在网上查找到有人同时开两个VS2005,一个用于触发,一个用于调试,呵呵,这个到是有新意,不过太烦琐了http://devtalk.dk/2007/10/27/Expand+And+Debug+A+Custom+BuildProvider.aspx

    后来转念一想,干嘛非得在BuildProvider类库的调试里折腾啊,我建一个web 站点程序,随便弄个页面,把BuildProvider当做普通的类来调试不就OK了么,下面是我调试ExtractInfo()方法。在最后一句设置断点,就可以跟踪到OrmBuildProvider 类里面了,呵呵。当然为了方便,把orm.map拷贝成一个orm.xml放在App_Code外面了,这样调试时好处理一些。

    protected void Button1_Click(object sender, EventArgs e)
    {
        OrmBuildProviderSln.OrmBuildProvider ormBuild = new OrmBuildProviderSln.OrmBuildProvider();
        string path = Request.ApplicationPath + "/orm.xml";
        ormBuild.ExtractInfo(path);
    }

     

    嗯,第一次写技术博客,写了这么多,不容易啊,以后正确多研究些,多记录些。刚开始研究,有什么不对的地方,请各位大牛批评指正。

    作者:峻祁连
    邮箱:junqilian@163.com
    出处:http://junqilian.cnblogs.com
    转载请保留此信息。
  • 相关阅读:
    分布式任务调度平台XXL-JOB
    在线文档分享工具 ShowDoc
    SVN 资源权限管理系统 SVNAdmin
    JFinal Weixin 微信极速 SDK
    Mybatis 增强工具包 Mybatis-Plus
    认识与入门:Markdown
    【Github】如何删除github上的项目
    【IntelliJ 】IntelliJ IDEA 自动导入包 快捷方式 关闭重复代码提示
    【Eclipse+IntelliJ反编译】Eclipse/IntelliJ IDEA反编译查看源码及注释
    【Eclipse】eclipse安装JAVA反编译插件
  • 原文地址:https://www.cnblogs.com/junqilian/p/1086652.html
Copyright © 2020-2023  润新知