<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      ORM查詢語(yǔ)言(OQL)簡(jiǎn)介--高級(jí)篇:脫胎換骨

         相關(guān)文章內(nèi)容索引:

          在寫本文之前,一直在想文章的標(biāo)題應(yīng)怎么取。在寫了《ORM查詢語(yǔ)言(OQL)簡(jiǎn)介--概念篇》、《ORM查詢語(yǔ)言(OQL)簡(jiǎn)介--實(shí)例篇》之后,覺得本篇文章應(yīng)該是前2篇的延續(xù),但又不是一般的延續(xù),因?yàn)榻裉煲獙懙倪@篇內(nèi)容,是基于對(duì)框架OQL完全重構(gòu)之后來(lái)寫的,所以加上一個(gè)副標(biāo)題:脫胎換骨!

       

      一、OQL之前生

      1.1,內(nèi)容回顧:

          OQL是我設(shè)計(jì)用來(lái)處理PDF.NET開發(fā)框架的ORM查詢的,因此叫做ORM查詢語(yǔ)言。自2006年第一版以來(lái),經(jīng)歷了多次重構(gòu),到PDF.NET Ver 4.X 版本,已經(jīng)比較穩(wěn)定了,在我做的項(xiàng)目和框架用戶朋友的項(xiàng)目中得到成功應(yīng)用,基本符合一般的常規(guī)應(yīng)用需求。

      OQL有下面3個(gè)顯著特點(diǎn):

      1. 抽象的SQL,屏蔽了具體數(shù)據(jù)庫(kù)的差異,因此支持所有數(shù)據(jù)庫(kù);
      2. 對(duì)象化的“SQL”,寫OQL代碼能夠獲得IDE的智能提示,能夠得到編譯時(shí)檢查確保不會(huì)寫出錯(cuò)誤的SQL;
      3. 沒有使用.NET的特性,比如泛型、反射、表達(dá)式樹等東西,因此理論上OQL可以跨語(yǔ)言平臺(tái),比如移植到Java,C++,VB等。

      OQL的原理基于2大特性:

      • 表達(dá)式的鏈?zhǔn)秸{(diào)用  
      • 屬性的實(shí)例調(diào)用

      OQL支持4大類數(shù)據(jù)操作

      • 數(shù)據(jù)查詢:
        • 單實(shí)體類(單表)查詢
        • 多實(shí)體類(多表)關(guān)聯(lián)查詢
      • 數(shù)據(jù)修改
        • 更新數(shù)據(jù)
        • 刪除數(shù)據(jù)
      • 統(tǒng)計(jì)、聚合運(yùn)算
      • OQL分頁(yè)

      1.2,老版本的局限

          盡管OQL已經(jīng)可以解決80%的查詢需求,剩下的20%查詢需求我都建議框架用戶使用SQL-MAP技術(shù)來(lái)完成,但對(duì)于用戶而言,是不太愿意從ORM模式切換到SQL模式的,希望OQL能夠解決盡可能多的查詢需求。那么,PDF.NET Ver 4.X 版本的OQL有哪些不足呢?

      1.2.1,自連接查詢

         也稱為表自身連接查詢。OQL支持多表(實(shí)體)查詢的,但卻無(wú)法支持自連接查詢,原因是自連接查詢必須指定表的別名:

      SELECT R1.readerid,R1.readername,R1.unit,R1.bookcount
      FROM ReaderInfo AS R1,ReaderInfo AS R2
      WHERE R2.readerid=9704
      AND R1.bookcount>R2.bookcount     --連接關(guān)系
      ORDER BY R1.bookcount

          上面這個(gè)查詢是從ReaderInfo表查詢可借圖書數(shù)目比編號(hào)為9704讀者多的所有讀者信息,這里對(duì)表使用了別名來(lái)實(shí)現(xiàn)的,如果不使用別名,那么這個(gè)查詢就無(wú)法實(shí)現(xiàn)。而OQL之前的版本,是不支持表的別名的,因此,對(duì)于連接查詢,OQL生成的可能是這樣子的SQL語(yǔ)句:

      SELECT teacher.*,student.* 
      FROM teacher  
        INNER JOIN student 
           ON teacher.id=student.tea_id

      1.2.2,子查詢

          老版本OQL僅支持IN條件的子查詢,不能像SQL那么靈活的進(jìn)行各種子查詢,其實(shí)不支持的原因其中一個(gè)也是因?yàn)镺QL查詢不支持表的別名,另外一個(gè)原因是子查詢無(wú)法獲取到父查詢的表名和字段名。子查詢是一個(gè)很常用的功能,如果不能夠支持,那么就大大限制了OQL的使用范圍。

          下面是來(lái)自SQLSERVER 聯(lián)機(jī)幫助的說(shuō)明:

      子查詢也稱為內(nèi)部查詢或內(nèi)部選擇,而包含子查詢的語(yǔ)句也稱為外部查詢或外部選擇。

      許多包含子查詢的 Transact-SQL 語(yǔ)句都可以改用聯(lián)接表示。其他問(wèn)題只能通過(guò)子查詢提出。在 Transact-SQL 中,包含子查詢的語(yǔ)句和語(yǔ)義上等效的不包含子查詢的語(yǔ)句在性能上通常沒有差別。但是,在一些必須檢查存在性的情況中,使用聯(lián)接會(huì)產(chǎn)生更好的性能。否則,為確保消除重復(fù)值,必須為外部查詢的每個(gè)結(jié)果都處理嵌套查詢。所以在這些情況下,聯(lián)接方式會(huì)產(chǎn)生更好的效果。以下示例顯示了返回相同結(jié)果集的 SELECT 子查詢和 SELECT 聯(lián)接:

      /* SELECT statement built using a subquery. */
      SELECT Name
      FROM AdventureWorks2008R2.Production.Product
      WHERE ListPrice =
          (SELECT ListPrice
           FROM AdventureWorks2008R2.Production.Product
           WHERE Name = 'Chainring Bolts' );
      
      /* SELECT statement built using a join that returns
         the same result set. */
      SELECT Prd1. Name
      FROM AdventureWorks2008R2.Production.Product AS Prd1
           JOIN AdventureWorks2008R2.Production.Product AS Prd2
             ON (Prd1.ListPrice = Prd2.ListPrice)
      WHERE Prd2. Name = 'Chainring Bolts';

      1.2.3,OQL數(shù)據(jù)插入

          盡管OQL可以支持實(shí)體類的批量更新與刪除,但沒有支持實(shí)體類的插入,原因是對(duì)單個(gè)實(shí)體類而言,可以直接調(diào)用EntityQuery<T>的Insert()方法實(shí)現(xiàn)。但項(xiàng)目中可能還是有需要寫SQL插入數(shù)據(jù)的情況,比如插入Int類型的值為0,如果用實(shí)體類的方式那么該列不會(huì)被插入,因?yàn)镻DF.NET的實(shí)體類認(rèn)為該屬性值沒有改變,PDF.NET的插入和更新操作,都只處理“屬性值改變過(guò)的”數(shù)據(jù)。另外,也不支持Insert....Select....From 這種批量插入方式。

      INSERT INTO MySalesReason
          SELECT SalesReasonID, Name, ModifiedDate
          FROM AdventureWorks2008R2.Sales.SalesReason
          WHERE ReasonType = N'Marketing';

       1.2.4,HAVING 子句

          OQL老版本不支持該功能,盡管不是很常用,但配合分組查詢還是有用的,如下面的例子HAVING 子句從 SalesOrderDetail 表中檢索超過(guò) $100000.00 的每個(gè) SalesOrderID 的總計(jì)。

      SELECT SalesOrderID, SUM(LineTotal) AS SubTotal
      FROM Sales.SalesOrderDetail
      GROUP BY SalesOrderID
      HAVING SUM(LineTotal) > 100000.00
      ORDER BY SalesOrderID ;

       

       二、浴火重生

          現(xiàn)在流行的ORM框架很多,剛剛在寫本文的時(shí)候,還發(fā)現(xiàn)有個(gè)朋友寫了一篇各ORM對(duì)比測(cè)試的文章:

          這么多ORM框架,我并不是很熟悉,PDF.NET的目標(biāo)只想在某些方面趕超MS的EF框架,據(jù)說(shuō)現(xiàn)在EF6都快出來(lái)了,EF4.5在性能上上了一個(gè)臺(tái)階。面對(duì)EF這個(gè)強(qiáng)敵,如果PDF.NET不能解決前面說(shuō)的幾大缺陷,注定距離會(huì)越來(lái)越遠(yuǎn),PDF.NET的用戶對(duì)我也是常常提出批評(píng),紛紛轉(zhuǎn)投EF去了,對(duì)此我深感壓力山大!

          盡管EF是PDF.NET ORM 的強(qiáng)勁對(duì)手,但 PDF.NET ORM的查詢語(yǔ)言O(shè)QL,相對(duì)于EF的查詢語(yǔ)言Linq,還是有自己獨(dú)立的特色,OQL比Linq更接近SQL,Linq是VS的語(yǔ)法糖,本質(zhì)上VS編譯器會(huì)將它轉(zhuǎn)化成Lambda表達(dá)式,進(jìn)一步轉(zhuǎn)換成表達(dá)式樹,最后翻譯成SQL語(yǔ)句交給數(shù)據(jù)庫(kù)去執(zhí)行。所以我們會(huì)看到針對(duì)集合操作的擴(kuò)展方法,有很多都要使用 => 的調(diào)用方式,而OQL沒有使用Lambda,它是怎么獲取到查詢對(duì)應(yīng)的表名稱和字段名稱的呢?它是怎么實(shí)現(xiàn)SQL查詢的層次結(jié)構(gòu)的呢?

      2.1,屬性字段的秘密

      PDF.NET屬性的定義采用下面的形式:

        public System.String UserName
              {
                  get { return getProperty<System.String>("UserName"); }
                  set { setProperty("UserName", value, 50); }
              }

      因此在獲取實(shí)體類的屬性值的時(shí)候,調(diào)用了getProperty<T>("字段名")   這個(gè)方法,它里面會(huì)觸發(fā)屬性讀取事件:

                /// <summary>
              /// 獲取屬性值
              /// </summary>
              /// <typeparam name="T">值的類型</typeparam>
              /// <param name="propertyName">屬性名稱</param>
              /// <returns>屬性值</returns>
              protected T getProperty<T>(string propertyName)
              {
                  this.OnPropertyGeting(propertyName);
                  return CommonUtil.ChangeType<T>(PropertyList(propertyName));
              }
      
              /// <summary>
              /// 屬性獲取事件
              /// </summary>
              public event EventHandler<PropertyGettingEventArgs> PropertyGetting;
              /// <summary>
              /// 獲取屬性的時(shí)候
              /// </summary>
              /// <param name="name"></param>
              protected virtual void OnPropertyGeting(string name)
              {
                  if (this.PropertyGetting != null)
                  {
                      this.PropertyGetting(this, new PropertyGettingEventArgs(name));
                  }
              }
      /// <summary>
          /// 屬性獲取事件
          /// </summary>
          public class PropertyGettingEventArgs : EventArgs
          {
              private string _name;
      
              /// <summary>
              /// 屬性名稱
              /// </summary>
              public string PropertyName
              {
                  get { return _name; }
                  set { _name = value; }
              }
      
              /// <summary>
              /// 以屬性名稱初始化本類
              /// </summary>
              /// <param name="name"></param>
              public PropertyGettingEventArgs(string name)
              {
                  this.PropertyName = name;
              }
          }
      PropertyGettingEventArgs

       

      而OQL實(shí)例對(duì)象,正是訂閱了EventHandler<PropertyGettingEventArgs> 事件:

       public OQL(EntityBase e)
              {
                  currEntity = e;
                 //其它代碼略
                  e.PropertyGetting += new EventHandler<PropertyGettingEventArgs>(e_PropertyGetting);
              }
      
      void e_PropertyGetting(object sender, PropertyGettingEventArgs e)
              {
                  //其它代碼略
               }

      所以,在屬性獲取事件中,我們可以通過(guò)PropertyGettingEventArgs.PropertyName 得到實(shí)體類屬性對(duì)應(yīng)的字段名稱,因此,我們就可以方便的做到選取我們本次查詢需要的字段,例如下面的OQL查詢:

      Users user = new Users();
      OQL q0 = OQL.From(user)
                  .Select(user.ID, user.UserName, user.RoleID)
                    .END;

      對(duì)應(yīng)的SQL語(yǔ)句:

      SELECT
           [ID],
           [UserName],
           [RoleID]
      FROM [LT_Users]

      這樣,我們無(wú)需使用委托,也不需要Lambda表達(dá)式,更不需要表達(dá)式樹,就能夠直接獲取到要查詢的表名稱和字段名稱,寫法比Linq更簡(jiǎn)潔,處理速度更快速。當(dāng)然,代價(jià)是要先實(shí)例化實(shí)體類。

      2.1,屬性獲取事件的變化

      在事件方法e_PropertyGetting 中,我們看看PDF.NET Ver 5.0前后的變化:

      Ver 4.X 以前:

              void e_PropertyGetting(object sender, PropertyGettingEventArgs e)
              {
                  doPropertyGetting(((EntityBase)sender).TableName, e.PropertyName);
              }
      
              private void doPropertyGetting(string tableName, string propertyName)
              {
                  if (isJoinOpt)
                  {
                      string propName = "[" + tableName + "].[" + propertyName + "]";
                      this.currJoinEntity.AddJoinFieldName(propName);
      
                  }
                  else
                  {
                      string field = this.joinedString.Length > 0 ? tableName + "].[" + propertyName : propertyName;
                      if (!hasSelected && !selectedFields.Contains(field))
                          selectedFields.Add(field);
                  }
                  this.getingTableName = tableName;
                  this.getingPropertyName = propertyName;
              }

          在doPropertyGetting  方法中,區(qū)分是否有實(shí)體類連接查詢,來(lái)處理不同的表名稱和字段名稱,這里看到連接查詢的時(shí)候沒有為表加上別名,而是直接使用了“表名稱.字段名稱”這種表示字段的形式。同時(shí),將當(dāng)前獲取到的表字段名,馬上賦值給getingPropertyName 變量。這帶來(lái)了一個(gè)問(wèn)題,屬性字段名稱必須馬上被使用,否則就會(huì)出問(wèn)題。
      由于不同的情況使用屬性字段的時(shí)機(jī)不一樣,為了處理這些不同的情況加入了各種Case下的處理代碼,比如將Select方法要使用的屬性字段名稱保存到列表  selectedFields 中。這種處理方法無(wú)疑大大增加了代碼的復(fù)雜度。

      Ver 5.0 版本的改進(jìn)

          前面說(shuō)到屬性獲取到的屬性字段名稱必須馬上被使用,否則就會(huì)出問(wèn)題。如果我們不論何種情況,都將這個(gè)屬性字段名先保存起來(lái)再使用呢?使用隊(duì)列?鏈表?堆棧?這些集合都可以,但在編譯原理中,對(duì)表達(dá)式的處理都是使用堆棧來(lái)做的,其中必有它的好處,以后會(huì)體會(huì)到。

      下面是屬性獲取事件代碼:

            void e_PropertyGetting(object sender, PropertyGettingEventArgs e)
              {
                  TableNameField tnf = new TableNameField()
                  {
                      Field = e.PropertyName,
                      Entity = (EntityBase)sender,
                      Index=this.GetFieldGettingIndex()
                  };
      
                  fieldStack.Push(tnf);
              }

          這里直接將屬性字段名存在TablenameField 結(jié)構(gòu)的Field字段中,然后將這個(gè)結(jié)構(gòu)壓入堆棧對(duì)象fieldStack 中,需要的時(shí)候在從堆棧中彈出最新的一個(gè) TableNameField 結(jié)構(gòu)。這樣,不論是OQL的Select方法,Where方法還是OrderBy方法,都能夠使用統(tǒng)一的堆棧結(jié)構(gòu)來(lái)獲取方法使用的屬性字段了。

      2.3,統(tǒng)一屬性獲取事件

      除了OQL本身需要“屬性獲取事件”,OQL關(guān)聯(lián)的OQLCompare對(duì)象,OQLOrder對(duì)象,都需要處理屬性獲取事件,比如之前實(shí)例化OQLCompare對(duì)象:

              /// <summary>
              /// 使用一個(gè)實(shí)體對(duì)象初始化本類
              /// </summary>
              /// <param name="e"></param>
              public OQLCompare(EntityBase e)
              {
                  this.CurrEntity = e;
                  //this.CurrEntity.ToCompareFields = true;
                  this.CurrEntity.PropertyGetting += new EventHandler<PropertyGettingEventArgs>(CurrEntity_PropertyGetting);
              }
      
              /// <summary>
              /// 使用多個(gè)實(shí)體類進(jìn)行連接查詢的條件
              /// </summary>
              /// <param name="e"></param>
              /// <param name="joinedEntitys"></param>
              public OQLCompare(EntityBase e, params EntityBase[] joinedEntitys)
              {
                  this.CurrEntity = e;
                  this.CurrEntity.PropertyGetting += new EventHandler<PropertyGettingEventArgs>(CurrEntity_PropertyGetting);
                  //處理多個(gè)實(shí)體類
                  if (joinedEntitys != null && joinedEntitys.Length > 0)
                  {
                      this.joinedEntityList = new List<EntityBase>();
                      foreach (EntityBase item in joinedEntitys)
                      {
                          this.joinedEntityList.Add(item);
                          item.PropertyGetting += new EventHandler<PropertyGettingEventArgs>(CurrEntity_PropertyGetting);
                      }
                  }
      
              }

      屬性獲取事件處理方法:

              void CurrEntity_PropertyGetting(object sender, PropertyGettingEventArgs e)
              {
                  if (this.joinedEntityList != null)
                  {
                      this.currPropName = "[" + ((EntityBase)sender).TableName + "].[" + e.PropertyName + "]";
                  }
                  else
                  {
                      this.currPropName = "[" + e.PropertyName + "]";
                      //propertyList.Add(e.PropertyName);
                  }
      
              }

      之所以要在OQLCompare等對(duì)象中也要處理屬性獲取事件,是為了OQLCompare能夠獨(dú)立使用,但這帶來(lái)一些問(wèn)題:

      • 各地的屬性獲取事件處理代碼類似,代碼有冗余;
      • 沒有體現(xiàn)出OQL跟OQLCompare 、OQLOrder對(duì)象之見的聚合性,呈現(xiàn)出松散的結(jié)構(gòu),因此可能出現(xiàn)OQLCompare使用的實(shí)體類在OQL中沒有使用,從而產(chǎn)生錯(cuò)誤的查詢;
      • OQLCompare中的的字段名與OQL缺乏相關(guān)性,因此只能通過(guò)“表名稱.字段名稱”這種形式來(lái)使用屬性字段名,無(wú)法使用別名。

       

      Ver 5.0的解決辦法:

      在OQL對(duì)象上,定義一些方法供OQL的關(guān)聯(lián)子對(duì)象來(lái)訪問(wèn)需要的屬性字段名信息:

             /// <summary>
              /// 從堆棧上只取一個(gè)字段名
              /// </summary>
              /// <returns></returns>
              protected internal string TakeOneStackFields()
              {
                 //其它代碼略
                    TableNameField tnf = fieldStack.Pop();
                  return GetOqlFieldName(tnf);
              }

       

      2.4,SQL的語(yǔ)法結(jié)構(gòu)

      SQL是結(jié)構(gòu)化查詢語(yǔ)言,它自身也是非常結(jié)構(gòu)化的,每一個(gè)查詢都有固定的語(yǔ)法結(jié)構(gòu),以Select為例,它可以有多種形式的寫法:

      SELECT Field1,Field2...    FROM [Table]
      
      -----------------
      
      SELECT Field1,Field2...    FROM [Table]
          WHERE Condition
      
      -----------------
      
      SELECT Field1,Field2...    FROM [Table]
          WHERE Condition
              ORDER BY FieldOrder 
      
      -----------------
      
      SELECT FieldGroup    FROM [Table]
          WHERE Condition
              GROUP BY FieldGroup
                  ORDER BY FieldOrder 
      
      ----------------
      
      SELECT FieldGroup    FROM [Table]
          WHERE Condition
              GROUP BY FieldGroup
                  HAVING havingExp
                      ORDER BY FieldOrder 
      
      -----------------
      
      SELECT Field1,Field2...    FROM [Table1]
      JOIN [Table2] ON [Table1].PK=[Table2].FK
          WHERE Condition
              ORDER BY FieldOrder 
      
      -----------------
      
      SELECT Field1,Field2...    FROM [Table1],[Table2]
           WHERE [Table1].PK=[Table2].FK And OtherCondition
              ORDER BY FieldOrder 
      
      -----------------

      仔細(xì)觀察,萬(wàn)變不離其宗,上面的寫法其實(shí)都是以下關(guān)鍵字所在層次結(jié)構(gòu)在不同情況下的組合而已,除了Select是必須的:

      SELECT FROM
          JOIN ON
          WHERE
              GROUP BY
                  HAVEING
                      ORDER BY

       

      2.5,OQL的層次結(jié)構(gòu)

          如果要以面向?qū)ο蟮姆绞絹?lái)實(shí)現(xiàn)SQL這個(gè)關(guān)鍵字層次結(jié)構(gòu),我們必須將相關(guān)的關(guān)鍵字作為方法,定義在合適的對(duì)象中,然后靠對(duì)象的層次結(jié)構(gòu),來(lái)限定正確的“SQL”結(jié)構(gòu),為此,我們先重新來(lái)定義一下OQL使用的接口IOQL和關(guān)聯(lián)的接口的層次定義:

      public interface IOQL
          {
              OQL1 Select(params object[] fields);
          }
      
          public interface IOQL1 : IOQL2
          {
              //OQL End { get; }
              //OQL3 GroupBy(object field);
              //OQL4 Having(object field);
              //OQL4 OrderBy(object field);
              OQL2 Where(params object[] fields);
          }
      
          public interface IOQL2 : IOQL3
          {
              //OQL End { get; }
              OQL3 GroupBy(object field);
              //OQL4 Having(object field);
              //OQL4 OrderBy(object field);
          }
      
          public interface IOQL3 : IOQL4
          {
              OQL4 Having(object field);
              //OQL End { get; }
              //OQL4 OrderBy(object field);
          }
      
          public interface IOQL4
          {
              OQL END { get; }
              OQLOrderType OrderBy(object field);
          }

      然后,讓OQL實(shí)現(xiàn)IOQL,在定義其他OQL子對(duì)象來(lái)實(shí)現(xiàn)其它子接口。由于對(duì)象比較多,還是通過(guò)一個(gè)對(duì)象結(jié)構(gòu)圖來(lái)看更方便:

       圖1:OQL接口層次圖

       

       

       

       圖2:OQL體系結(jié)構(gòu)圖

       

       2.6 OQLCompare--比較對(duì)象的組合模式

      SQL的查詢條件可以很簡(jiǎn)單,也可以很復(fù)雜,比如下面的復(fù)合查詢條件:

      SELECT  M.*,T0.*  
      FROM [LT_Users]  M  
      INNER JOIN [LT_UserRoles] T0  ON  M.[RoleID] = T0.[ID] 
           WHERE 
          (   M.[UserName] = @P0 AND  M.[Password] = @P1  AND  T0.[RoleName] = @P2 )
       OR 
          (
            (
               M.[UserName] = @P3 AND  M.[Password] = @P4  AND  T0.[ID]  IN  (1,2,3) 
            ) 
          OR 
           M.[LastLoginTime] > @P5 )

          這個(gè)查詢條件分為2組條件,然后第二組查詢內(nèi)部又包含2組查詢,從括號(hào)層數(shù)來(lái)說(shuō),僅僅有3層,但看起來(lái)已經(jīng)夠復(fù)雜了。實(shí)際項(xiàng)目中,我曾遇到過(guò)用5000行業(yè)務(wù)代碼來(lái)構(gòu)造SQL查詢條件的情況,不要吃驚,的確是5000行業(yè)務(wù)代碼,當(dāng)然不是說(shuō)SQL條件有5000行,但也可以想象到,最終生成的SQL查詢條件的長(zhǎng)度不會(huì)小于50行。這樣復(fù)雜的查詢條件,如果用拼接SQL字符串的方式來(lái)完成,工作量是不可想象的,維護(hù)起來(lái)也是非常困難。但是,我們可以利用OQL的查詢條件對(duì)象OQLCompare來(lái)完成,因?yàn)樗鼘?shí)質(zhì)上是一個(gè)組合對(duì)象,即N多個(gè)OQLCompare組合成一個(gè)OQLCompare對(duì)象,不過(guò)為了實(shí)現(xiàn)方便,我們規(guī)定每個(gè)OQLCompare對(duì)象下面存放2個(gè)子對(duì)象,也就是建立一個(gè)二叉樹來(lái)存儲(chǔ)所有的比較對(duì)象:

      public class OQLCompare
      {
      
      //其它代碼略
      
              protected OQLCompare LeftNode { get; set; }
      
              protected OQLCompare RightNode { get; set; }
      
              protected CompareLogic Logic { get; set; }
      
              protected bool IsLeaf
              {
                  get
                  {
                      return object.Equals(LeftNode, null) && object.Equals(RightNode, null);
                  }
              }
      
              protected string ComparedFieldName;
              protected string ComparedParameterName;
              protected CompareType ComparedType;
      
      }

      還是用一張圖來(lái)看看查詢條件的構(gòu)成比較直觀:

       

       圖3:OQLCompare 對(duì)象樹

          該圖的內(nèi)容,說(shuō)明了構(gòu)造上面的SQL條件的OQLCompare比較對(duì)象的樹型結(jié)構(gòu),我們規(guī)定,每個(gè)節(jié)點(diǎn)下面只有左節(jié)點(diǎn)和右節(jié)點(diǎn),左節(jié)點(diǎn)優(yōu)先,左右子節(jié)點(diǎn)都可以是空,如果符合該條件,則當(dāng)前節(jié)點(diǎn)為葉子結(jié)點(diǎn),否則為枝節(jié)點(diǎn)。

          從上圖可以很容易發(fā)現(xiàn),其實(shí)這就是一個(gè)“組合模式”,而組合模式的每個(gè)節(jié)點(diǎn)都具有相同的行為和特性,所以,我們可以構(gòu)建非常復(fù)雜的組合體系,最終構(gòu)造超級(jí)復(fù)雜的查詢條件,而在最終使用上,一組查詢條件跟一個(gè)查詢條件的處理過(guò)程是一樣的。

      2.7,條件表達(dá)式的括號(hào)問(wèn)題

          括號(hào)是控制表達(dá)式計(jì)算順序的重要手段,對(duì)于邏輯表達(dá)式,使用AND,OR 來(lái)連接兩個(gè)子表達(dá)式,如果AND,OR同時(shí)出現(xiàn),則需要用括號(hào)來(lái)改變表達(dá)式元素計(jì)算的順序。C,C++,C# 對(duì)表達(dá)式都是“左求值計(jì)算”的,這是一個(gè)很重要的概念,某些程序語(yǔ)言可能是“右求值計(jì)算”的。如果表達(dá)式中有括號(hào),那么前面的計(jì)算將掛起,計(jì)算完括號(hào)內(nèi)的結(jié)果后,再繼續(xù)處理表達(dá)式的剩余部分。因此,我們可以把括號(hào)看作一個(gè)“樹枝節(jié)點(diǎn)”,而括號(hào)內(nèi)最內(nèi)層的節(jié)點(diǎn),為葉子結(jié)點(diǎn),按照我們對(duì)節(jié)點(diǎn)類型的定義和上面示例的OQLCompare條件組合樹,在輸出SQL條件字符串的時(shí)候,可能是這個(gè)樣子的:

      SELECT  M.*,T0.*  
      FROM [LT_Users]  M  
      INNER JOIN [LT_UserRoles] T0  ON  M.[RoleID] = T0.[ID] 
           WHERE 
          ((  ( M.[UserName] = @P0 AND  M.[Password] = @P1)  AND  T0.[RoleName] = @P2 )
       OR 
          (
            (
               (M.[UserName] = @P3 AND  M.[Password] = @P4)  AND  T0.[ID]  IN  (1,2,3) 
            ) 
          OR 
           M.[LastLoginTime] > @P5 ) )

      假設(shè)條件表達(dá)式需要對(duì)10個(gè)字段的比較內(nèi)容進(jìn)行AND 判斷,那么將會(huì)嵌套10-1=9 層括號(hào)。

      不要小看這個(gè)問(wèn)題,前面我說(shuō)到的那個(gè)5000行業(yè)務(wù)代碼構(gòu)建SQL查詢條件的事情,就曾經(jīng)發(fā)生過(guò)構(gòu)造了128層括號(hào)的事情,最終導(dǎo)致SQLSERVER報(bào)錯(cuò):

      查詢條件括號(hào)嵌套太多,查詢分析器無(wú)法處理!

      那么括號(hào)怎么化簡(jiǎn)呢?

      這個(gè)得從表達(dá)式的邏輯語(yǔ)義上去分析:

      • (A AND B) AND C <==> A AND B AND C
        • (A OR B) OR C <==> A OR B OR C
          • (A AND B) AND (C AND D) <==> A AND B AND C AND D
            • (A OR B) OR (C OR D) <==> A OR B OR C OR D

       

       

      所以,我們可以檢查“子樹枝節(jié)點(diǎn)”的邏輯比較類型,如果它的類型與當(dāng)前節(jié)點(diǎn)的邏輯比較類型相同,那么對(duì)子樹枝節(jié)點(diǎn)的處理就不需要使用括號(hào)了。

      可以通過(guò)哦遞歸過(guò)程,處理完所有的子節(jié)點(diǎn)的括號(hào)問(wèn)題,從而最終得到我們看起來(lái)非常簡(jiǎn)單的條件表達(dá)式。

       

       (本文篇幅太長(zhǎng),未完待續(xù))

       

      posted on 2013-07-26 17:26  深藍(lán)醫(yī)生  閱讀(12401)  評(píng)論(28)    收藏  舉報(bào)

      導(dǎo)航

      主站蜘蛛池模板: 国产高清亚洲一区亚洲二区| 精品国产成人a在线观看| 国产精品夫妇激情啪发布| 国产一区二区不卡在线看| 中文字幕v亚洲日本在线电影| 艳妇乳肉豪妇荡乳av无码福利| 老熟女重囗味hdxx69| 精品国产午夜福利理论片| 亚洲乳大丰满中文字幕| 香蕉久久一区二区不卡无毒影院| 久热这里只有精品12| 丁香五月天综合缴情网| 亚洲av永久无码精品网站| 18禁美女裸体爆乳无遮挡| 亚洲中文字幕无码久久精品1| 亚洲中文字幕人成影院| 亚洲国产精品综合久久20| 国产精品无码a∨麻豆| 狠狠色综合久久狠狠色综合| 赣榆县| 亚洲国产成人精品无色码| 国产熟女一区二区五月婷| 99视频30精品视频在线观看| av无码小缝喷白浆在线观看| 部精品久久久久久久久 | 成人区人妻精品一区二区| 国产精品中文一区二区| 麻豆a级片| 福利视频在线一区二区| 97se亚洲国产综合自在线观看| 亚洲综合成人一区二区三区| 国产女人喷潮视频免费| 普陀区| 性色av一区二区三区精品| 亚洲国产一区二区三区四| 国产裸体美女视频全黄| av在线播放国产一区| 久久老熟女一区二区蜜臀| 亚洲国产大胸一区二区三区| 久久伊99综合婷婷久久伊| 亚洲成色精品一二三区|