1. 程式人生 > 其它 >如何用ORM支援SQL語句的CASE WHEN?

如何用ORM支援SQL語句的CASE WHEN?

OQL如何支援CASE WHEN?

今天,一個朋友問我,OQL可否支援CASE WHEN語句?他給的示例SQL如下:

select HName,case when IsEnable=1 then '啟用' else '停用' from tb_User

OQL是SOD框架ORM查詢語言,它類似Linq,但是它誕生的歷史比Linq早,並且更加接近SQL語法。所以,對SOD框架而言,對應ORM如何支援CASE WHEN,就等於是問OQL如何支援CASE WHEN了。

這個問題已經不止這一個朋友來問我了,我想了下,還是把這個問題的解決寫一篇部落格,給大家一種新的解決方案。 注意“新的方案”這個提法,我是不打算讓OQL支援CASE WHEN這個特性的,為何要這樣做呢?

  1. OQL只解決 80%的普通查詢,其它複雜的查詢,應該依託於其它技術,否則會增加OQL的複雜性;
  2. 複雜的查詢,可以藉助於SOD框架的SQL-MAP技術,將SQL語句寫在SqlMap.config檔案中;
  3. 可以採用資料庫檢視或者表的計算列,但有些資料庫可能不支援計算列;

前面3種原因,第2,3條方法也可以看做是此問題的解決方案,但是它們都需要增加更多的工作量,如果OQL能夠直接支援還是更方便些,所以,我今天在這裡給大家第4種解決方案:

實體類的計算屬性

廢話不多說,先直接看程式碼:

public class User:EntityBase
{
  public User()
  {
      TableName="tb_User";   
  }

  public string HName{
    get{return getProperty<string>("HName");}
    set{setProperty<string>("HName",value,50);}
  }

  public bool IsEnable{
    get{return getProperty<bool>("IsEnable");}
    set{setProperty<bool>("IsEnable",value);}
  }
  
  public string IsEnableDescrition
  {
     get{
        return IsEnable?"啟用":"停用"
     }
   }

}

在這裡,HName,IsEnable 這樣的屬性,SOD框架稱為“持久化屬性”,因為它MAP了Tb_User表的欄位IsEnable ,該欄位稱為“持久化屬性欄位”。

持久化屬性的get,set方法採用了SOD實體類特殊的方法 setProperty,getProperty 構造,所以屬性IsEnableDescription 它不是持久化屬性,但是它利用了IsEnable這個持久化屬性,對持久化屬性進行“計算”,因此,我們這裡稱呼這樣的屬性為實體類的“計算屬性”。

大家看看,這個“計算屬性”是不是很好的起到了 SQL的CASE WHEN效果?

只要忘記了資料庫,不要遇到問題就去想如何用SQL語句解決,是不是思路豁然開朗?

使用“計算屬性”來支援CASE WHEN效果

前面說過,實體類的“計算屬性”本質上不是一個“持久化屬性”,它是對持久化屬性的計算處理,原理上非常類似SQLServer表上面的計算列。 因此,在SOD框架上使用“計算屬性”,有一個必須注意的原則:“計算屬性”不可以出現在OQL語句中。

具體舉例來說,應該像下面的樣子來使用包含計算列的實體類:

User user=new User(){ HName="張三"};
var q=OQL.From(user)
  .Select(user.HName,user.IsEnable)
  .Where(user.HName)
.END;

User user2= EntityQuery<User>.QueryObject(q);
string isEnableDesction =user2.isEnableDesction;

使用“ViewModel”來支援CASE WHEN效果

如果再仔細看看開篇的這個SQL語句,我們發現這種寫法常常跟我們的介面查詢有關,也就是這個查詢要將原來的結果進行一下加工,以方便介面元素使用。對應這種需求,我們通常想到的辦法是寫一個ViewModel來定製這個查詢結果。實際上,前面那個SOD實體類就是一種ViewModel,但它是基於實體類上的ViewModel,之外,SOD也支援直接將查詢結果對映到ViewModel。

因此,前面的實體類需要改寫成下面這個樣子:

public class User:EntityBase
{
  public User()
  {
      TableName="tb_User";   
  }

  public string HName{
    get{return getProperty<string>("HName");}
    set{setProperty<string>("HName",value,50);}
  }

  public bool IsEnable{
    get{return getProperty<bool>("IsEnable");}
    set{setProperty<bool>("IsEnable",value);}
  }
  
}

這裡去掉了前面的“計算列屬性”,它將用一個匿名型別的屬性來代替:

User user=new User(){ HName="張三"};
var q=OQL.From(user)
    .Select()
    .Where(user.HName)
.END;

 PWMIS.DataProvider.Data.AdoHelper db = PWMIS.DataProvider.Adapter.MyDB.GetDBHelper();
 EntityContainer ec = new EntityContainer(q, db);
 var list = ec.MapToList()(()=> return new
     {
        HName = user.HName,
        IsEnableDescription =user.IsEnable?"啟用":"停用"
     });

這裡的匿名型別中包含了 IsEnableDescription 一個屬性,同時我們的OQL查詢也不再需要在Select裡面指定查詢的屬性,而是推遲到MapToList方法裡面。

上面這種查詢方式,同樣支援了我們標題的需求,可見,SOD對一個查詢問題,往往能夠提供多種解決方案,“條條道路通羅馬”,這正是SOD的設計諫言。

---------------分界線--------------------------------------------------------------------------------

有關上面SOD框架查詢使用的入門介紹,大家可以參考《.NET ORM 的 “SOD蜜”--零基礎入門篇 》

感謝大家對SOD框架一直以來的支援,更多資訊,請參考 PDF.NET SOD 開源框架紅包派送活動 && 新手快速入門指引

開源專案需要大家更多的支援,SOD開源專案網站:http://pwmis.codeplex.com