版本
1.2 [2006-11-12]
簡介
本教程在前一篇《NBearV3 Step by Step教程——ORM篇》的基礎上,演示前文中沒有涉及的實體自關聯關系;及和性能相關的Attribute的設置:IndexProperty,BatchUpdate;並將詳細比較和討論NBear.Data.Gateway類中的強類型查詢方法的使用和注意事項,特別是在不同的Attribute設置選項下的性能差異分析。
注:在閱讀本文之前,建議讀者先閱讀《NBearV3 Step by Step教程——ORM篇》以掌握NBearV3中有關ORM的基本知識。
目標
通過本教程,讀者應能夠更全面地掌握使用NBearV3的ORM模塊進行應用程序設計的過程,了解實體設計中與性能相關的重要選項,並能全面掌握NBear.Data.Gateway中各種強類型查詢方法的使用。
代碼
本教程演示創建的所有工程和代碼,包含於可以從sf.net下載的NBearV3最新源碼zip包中的tutorials\ORM_Adv_Tutorial目錄中。因此,在使用本教程的過程中如有任何疑問,可以直接參考這些代碼。
時間
<30分鐘。
正文
Step 1 下載NBearV3最新版本及准備
1.1訪問http://sf.net/projects/nbear,下載NBearV3的最新版本到本地目錄。
1.2 將下載的zip文件解壓至C:\,您將看到,加壓後的NBearV3目錄中包括:dist、doc、cases、src、tutorials等目錄。其中,在本教程中將會使用的是dist目錄中的所有release編譯版本的dll和exe和tutorials目錄中之前的ORM基礎教程。
1.3 將tutorials目錄中的整個ORM_Tutorial目錄復制到任意其它位置,並命名為ORM_Adv_Tutorial,我們將以ORM_Tutorial為基礎,演示NBearV3中的ORM的進階知識。
Step 2 擴展設計實體及元數據
2.1 將ORM_Adv_Tutorial中的ORM_Tutorial.sln重命名為ORM_Adv_Tutorial.sln,並在VS2005開發環境中打開。
2.2 在EntityDesigns工程的Group.cs文件中,添加ParentID屬性,並為ParentID屬性添加IndexPropertyAttribute設置如下:
[IndexProperty]
Guid ParentID
{
get;
set;
}
將屬性標記為IndexProperty,表示在NBear.Tools.EntityDesignToEntity.exe生成的數據庫創建腳本中,將自動為該屬性創建一個單列索引。我們都理解數據庫索引的作用——這將有助於改善以該屬性為條件的查詢性能。
2.3 還是在Group.cs文件中,我們為Group新增一個SubGroups屬性如下:
[FkQuery("ParentID", Contained=true, LazyLoad=true)]
Group[] SubGroups
{
get;
set;
}
我們可以看到,SubGroups屬性的設置方式和普通的1對多關聯的屬性設置方式沒什麼兩樣,但是,可以注意到,它其實是Group實體1對多關聯到Group實體自己,也就是說,它是一個1對多的自關聯。
注:對於1對1自關聯,也可以以類似的方式設置。但是,NBearV3還不支持多對多的自關聯,也不支持一個繼承體系中的類的多對多關聯,例如,不支持User和LocalUser的多對多關聯。
2.4 在User.cs文件和LocalUser.cs中,為User和LocalUser實體添加一個BatchUpdateAttribute如下:
[BatchUpdate(10)]
public interface User : Entity
{
\\...
}
[BatchUpdate(10)]
public interface LocalUser : User
{
\\...
}
BatchUpdate這個Attribute對於該實體的查詢性能將有重要影響。當使用BatchUpdate標記一個實體時,實體的Save和Delete級聯操作,將在內部自動使用一個BatchGateway,以批更新方式執行。BatchUpdate的構造參數代表每多少個作為一個batch,連接數據庫,並執行。舉例來說,假如原來,User實體的一個實例Delete的時候,由於User實體自己,及所有User中以Contained這個Attribute標記的屬性對象的數量一共有20個應該被自動Delete掉,那麼,要連接20次數據庫,執行20次Delete;而在設置了BatchUpdate(10)的情況下,因為每十個查詢才連接一次數據庫,所以,實際上,只需要連接兩次數據庫,每次分別發送並執行10個Delete操作。很顯然,BatchUpdate對性能的改善是驚人的。
Step 3 從實體設計代碼生成實體代碼、實體配置文件和數據庫生成腳本
3.1 至此,所有的實體的設計就擴展完畢了。編譯EntityDesigns工程。
3.2 運行dist目錄中的NBear.Tools.EntityDesignToEntity.exe工具,載入EntityDesigns工程編譯生成的EntityDesigns.dll。
3.3 點擊Generate Entities按鈕,將生成的代碼保存到Entities工程中的一個名叫Entities.cs的新代碼文件。
3.4 點擊Generate Configuration按鈕,將生成的代碼保存到website工程下的名為EntityConfig.xml的文件中。
3.5 點擊Generate DB Script按鈕,將生成的代碼保存到website工程下的名為db.sql的文件,可以在某個新建的SQL Server數據庫中執行這些腳本,創建對應於所有實體的數據庫腳本。對於本教程而言,建議使用SQL Server 2000的查詢分析器,在tempdb數據庫中執行db.sql中的腳本。
Step 4 使用Gateway進行查詢
4.1刪除website工程中的Default.aspx.cs文件中原來的代碼,參考tutorials\ ORM_Adv_Tutorial\website目錄下的Default.aspx和Default.aspx.cs,創建Default.aspx。這些代碼演示了Gateway類的大多數強類型查詢方法的使用。關於Gateway支持的更多方法的介紹,可以參考doc目錄下的SDK類庫文檔。
4.2 運行以上代碼,在輸出文本框內,您將得到詳細的SQL調試信息及相關說明。
在運行結果中,大家可以注意比較以“TEXT 內容”格式出現的SQL日志,這些日志顯示了何時,NBearV3的強類型查詢在內部執行了什麼樣的SQL查詢。
有兩個地方需要特別留意比較的,一個是BatchUpdate後非BatchUpdate方式下的Save和Delete時,實際執行的SQL語句;另一個是,注意LazyLoad=true和LazyLoad=false的屬性,載入的時機的不同。
正文結束。
附錄
1 關於Entity.Attach()/Detach()/IsAttached()方法的使用
細心的讀者會發現,NBearV3中,保存實體只有Save方法,而不區分Insert還是Update。那麼,是如何區分一個實體什麼時候該Insert,什麼時候該Update的呢?
NBear判斷的是Entity.IsAttached()方法。一個剛剛new出來的實體實例,總是IsAttached() == false的,因此,當Save它時,實際會執行Insert,而一個通過Find返回的實體實例,則總是IsAttached() = true的,當保存這樣的實體時,只有修改過的屬性會被Update。
理解以上這一點有什麼作用呢?在需要的情況下,我們可以靈活地直接調用Entity.Attach()/Detach()來修改IsAttached()的值,從而實現一些特殊需求的查詢。
例如:
假如我們想更新User對象的Status,而我們的輸入參數只有User的id。如果不了解Attach()/Detach()方法,那麼,只有先通過Gateway.Find方法找到ID=id的User實例,修改Status,再Save它。這至少需要兩次數據庫查詢,一次Select,一次Update。更有效的做法是,我們可以new一個User的實例user,手動設置這個user.Attach(),修改user的Status,再Save它。這樣,這個user被保存時,也只會Update ID為id的User的Status屬性,但是,此時,只需要一次數據庫查詢,就是Update。
相反的,Detach()方法,可以設置一個Attached的實體實例的IsAttached() = =false。這有什麼用呢?
例如:
我們已經通過某些渠道得到了一個User的實例user,在某個算法中,我們可能要頻繁的寫User的屬性,最後,才會保存這個user。如果不設置Detach(),則實體屬性的每次修改都會觸發Entity.OnPropertyChanged事件,這不僅意味著性能損失,也意味著,Entity.OnPropertyChanged的訂閱者可能要響應很多不必要的事件變更事件。此時,理想的方法是,在頻繁的寫User的屬性的算法執行之前,我們先Detach(),算法完成後,我們再Attach()。
2 Gateway.RegisterSqlLogger()/UnregisterSqlLogger()
您應該已經注意到,Default.aspx.cs文件中的Page_Load中Gateway.RegisterSqlLogger()的使用:
gateway.RegisterSqlLogger(new LogHandler(WriteLine));
對應於注冊一個SqlLogger,我們也可以調用UnregisterSqlLogger()來注銷一個SqlLogger。
這兩個方法對於調試應用程序的運行和程序性能的優化非常重要,建議大家在開發中一定要注意使用,從而有效監視NBear提供的強類型外衣下的,真正的程序和數據庫的交互細節。
//本文結束