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這個特性的,為何要這樣做呢?
- OQL只解決 80%的普通查詢,其它復雜的查詢,應該依托於其它技術,否則會增加OQL的復雜性;
- 復雜的查詢,可以借助於SOD框架的SQL-MAP技術,將SQL語句寫在SqlMap.config文件中;
- 可以采用數據庫視圖或者表的計算列,但有些數據庫可能不支持計算列;
前面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