今天去客戶那邊的時候,正好對方問起幾種數據訪問方式的優劣,索性在此總結一下。本篇博客中不涉及到什么新的技術,也沒有代碼示例,只是從原理上解釋一下各種數據訪問方式,從而對比出這幾種方式之間的優劣。
在SharePoint 2010中,基本上有如下幾種數據訪問方式:
- 服務器端對象模型
- LINQ to SharePoint
- Web Service
- 客戶端對象模型
- ADO.NET Data Service (REST協議)
- ADO.NET Data Service (強類型)
當然,還有比如通過WebDav和RPC這種用的人很少(SharePoint內部、以及SPD里面倒是有不少地方在用),而且功能也比較局限的訪問方式就暫且不提了。還有就是PowerShell這種使用場景比較窄的方式,也暫且不提了。
1、服務器端對象模型(Server Object Model)
這個是應用的最為廣泛的數據訪問方式,其功能上也是最完整的。實際上,SharePoint幾乎所有自己的控件、Web部件、頁面都是通過這一套對象模型寫出來的(當然,可能用到了其中的一些internal的方法或者屬性)。之前我去培訓的時候,在介紹SharePoint開發概述時總說的一句話就是:在SharePoint默認頁面里面通過點擊鼠標鍵盤能完成的操作,絕大多數情況下使用服務器端對象模型都能完成;而且也有很多操作,你通過SharePoint內置的UI實現不了,但是通過Server OM也可以實現(比如跨網站的查閱項、多列表關聯查詢、視圖中的若干篩選、排序條件等等等等,數不勝數)。
服務器端對象模型顧名思義,是只能運行在服務器端的(換句話說,只能運行在裝了SharePoint的機器上,而且一般情況下只能訪問當前這個場的資源),歸根結底是從Web前端服務器(WFE)直接訪問后台數據庫的(對於那些Service Application的API而言,是通過前端的Proxy連接到應用程序服務器的WCF接口的,在后面的介紹中,我們暫且忽略這種場景)。如下圖所示:
因為WFE和DB之間有良好的帶寬保障,因此在做一些數據訪問和操作的時候,可能會造成多次數據往返,而且往返的內容可能比你想象的要多(因此在所有的SharePoint部署場景中,強烈建議把前端和數據庫安排在同一個局域網的同一個網段內,充分保證其帶寬)
此外,在SharePoint數據庫中的所有數據操作都是通過存儲過程來完成的,因此,除非你100%地確定后果,千萬不要直接去修改SharePoint數據庫里面的數據。
2、LINQ to SharePoint
SharePoint本身的那些SPxxxxCollection類中,並不是所有的都實現了IEnumerable<T>的接口,因此傳統的LINQ to Object並不能用在大多數常用的對象上。這里面的LINQ to SharePoint特指針對SharePoint列表條目的查詢。(客戶端對象模型有自己的類似LINQ to Object的能力,本文不討論這一點)
LINQ to SharePoint在2007的時候是一個CodePlex上面的開源項目(第三方的),據說后來這個作者就被挖到微軟去了(坊間八卦,真假不明)。
在SharePoint 2010中,LINQ to SharePoint需要開發人員通過SPMetal.exe這個命令行工具生成一個實體的DataContext類(或者你足夠熟悉這一套框架的話,自己寫也行)。LINQ在做查詢的時候,會把大部分的查詢翻譯成CAML語句,然后交給傳統的SPQuery去執行,再把返回結果處理成實體類對象,如下圖所示:
SPQuery是個服務器端API,所以LINQ to SharePoint也是僅能夠運行在服務器端(那個SPMetal工具倒是可以通過客戶端訪問),嚴格意義上說,這個應該算是服務器端對象模型的一部份。
LINQ to SharePoint的優勢有三個:第一、實體類,在編程的時候更直觀;第二、在處理復雜查詢,尤其是多列表聯合查詢的時候,編寫查詢條件更容易和直觀;第三、LINQ to SharePoint不僅支持數據的查詢,同時也支持數據的添加、修改和刪除,並且可以像強類型數據集那樣,通過SaveChanges方法,把所有的修改一次性地提交到網站中。
但是LINQ to SharePoint本身也會在一定程度上造成性能損失,主要原因有二:第一、把數據轉換成實體類,在數據量很大的時候,這個開銷是難以忽視的;第二、把查詢轉換成CAML查詢,這本來是個很小的開銷,但是SharePoint 2010在處理有些LINQ語句(比如FirstOrDefault這種),不能“正確”地處理成最優的CAML查詢,可能會把LINQ to SharePoint退化成LINQ to Object,也就是先返回並實體化列表中的所有數據,然后再在內存中做查詢,從而造成極大的性能問題。
3、Web Service
Web Service是SharePoint中非常傳統的一種數據訪問方式,從2003的時候就有(2001我沒有接觸過,不確定有沒有,歡迎用過2001的人補充),而且一直以來變化都不大(美其名曰向前兼容)。
這些Web Service接口都是在_vti_bin這個虛擬目錄(物理目錄在14\ISAPI)中的若干個asmx,和客戶端連接的時候走的是標准的SOAP協議,也就是說在傳遞過程中用的都是Xml,當然在其后台,執行的邏輯依然是通過服務器端API去完成的,如下圖所示:
上圖中需要注意的一個特點,就是在每一次執行Web Service中某個方法的時候,都和服務器有數據交互,這一點和后面提到的某幾種方式有很大區別。
SharePoint 2010中,對於大多數常見操作,多多少少都會有Web Service接口,不僅包括最常用的網站、列表、列表數據、工作流的操作(雖然某些操作在處理起來的時候會比較別扭,典型的一個例子就是通過Copy.asmx實現文件上載;此外不建議使用Web Service來設置權限,由於歷史原因造成了寫法非常不標准),也包括了在標准版或企業版中才有的對於用戶配置文件、企業搜索、Excel Service等服務的常規操作,操作的涵蓋面還是比較廣的。
但是Web Service在編寫的過程中,有如下兩個問題:
第一、所有操作的處理結果都是Xml格式,也就是說,如果需要獲取數據的話,需要你自己去解析這些Xml,而且涉及到列表字段的時候,基本上都要使用內部名稱,對於稍微復雜一些的操作(比如列表查詢等),在傳遞參數的時候,也是要傳遞一個XmlNode類型的參數,而不是一個簡單的string;
第二、由於Web Service在返回的時候會返回Xml格式,而且很難去控制要返回哪些屬性(比如在獲取列表的時候,可能會把列表里面的各種架構、屬性都一次性返回),如果程序處理不當,很容易造成返回結果非常龐大,這直接就帶來了兩個后果:(1)如果客戶端和服務器端之間的網絡帶寬有限的話,可能會對操作的速度有所影響;(2)很容易造成返回超時或者數據過大的錯誤。我們在一些項目里面就曾經遇到過類似的問題,比如一次性獲取上千條列表條目的時候,就會出現異常,我們不得不通過分頁的方式把數據拆分成多次獲取,但是這樣一來又無可避免地增加了程序的復雜度。
4、客戶端對象模型
客戶端對象模型是SharePoint 2010新增的一個開發特性(當然,在2007的時候,也有一些第三方的手段,比如我寫的這個:SharePoint JavaScript Lib),根據使用環境的不同,可以分為.NET客戶端對象模型、Silverlight客戶端對象模型、JavaScript客戶端對象模型。后面兩種在SharePoint 2010中,依然必須把silverlight或者js腳本放在SharePoint網站里面才能正常運行(但是實際的運行環境是發生在客戶端的瀏覽器進程里面,所以也算是客戶端訪問),所以這里面我們就只以.NET客戶端對象模型來進行說明。
幾種不同方式的客戶端對象模型都有自己的運行時環境,但是他們都有統一的數據訪問層,那就是位於_vti_bin下(沒錯,跟Web Service在一個目錄里)的Client.svc這個WCF接口,其具體的執行原理如下:
從圖中我們可以看到,在執行客戶端API的時候,並不會和服務器端發生交互,直到當我們執行了ExecuteQuery這個方法(或者它的異步版本ExecuteQueryAsync)的時候,客戶端運行時環境會把之前所有的操作序列“翻譯”成一段Xml,發送到服務器端的WCF接口,然后服務器端通過服務器端對象模型依次執行其中的操作,然后把我們所需的結果通過JSON的格式返回給客戶端運行時,再由客戶端運行時把這個JSON翻譯成相應的對象。——這是客戶端對象模型訪問時候的一大特點。
它的另一個特點,在於我們在獲取返回數據的時候,需要“顯式”地指定我們所需的結果(通過Load方法),甚至可以通過Load方法的一些重載,去指定我們所需對象的屬性、字段(比如只獲取列表的標題屬性、只獲取列表條目的某幾個字段值等等)。而服務器端在返回結果給客戶端的時候,會根據我們事先聲明的返回需要,只傳遞那些我們需要的值回來。
通過上述兩個特點,我們就可以看出,客戶端對象模型的一個優勢,就在於它可以把客戶端和服務器端數據傳遞的次數和大小都降至最低,從而在帶寬難以保證的情況下獲得較好的執行效率,也可以一次性返回更多的內容回來。
不過客戶端對象模型的功能也是有局限的,在SharePoint 2010中,客戶端對象模型只包含了SharePoint Foundation的功能,包括網站、列表結構、列表數據、權限、用戶、文件、文件夾等等。對於標准版或者企業版中提供的一些功能,目前在客戶端對象模型中沒有提供相應的接口。
5、ADO.NET Data Service(REST協議)
這依然是SharePoint 2010新增的一個接口,位於_vti_bin目錄(好吧,它們都在一起)中的listdata.svc。
REST協議的一個特點,就是可以把我們需要的操作都濃縮在一個URL中(比如通過:_vti_bin/listdata.svc/Employee(3)就可以獲取到Employee這個列表中ID為3的那個條目的信息,當然也可以通過一些特殊的寫法實現簡單的查詢、排序功能),並可以通過GET、POST、PUT、DELETE、MERGE這幾種HTTP訪問方式,來實現列表數據的增、刪、查、改。
通過REST協議訪問數據的原理如下圖所示:
通過這種方式訪問的時候,客戶端使用JSON的格式把需要的數據通過某種HTTP方法發送至包含listdata.svc的Url地址上(如果是Get方法的話,就沒有發送的數據),然后返回數據的時候,可以返回Xml格式(Atom協議),也可以返回JSON格式(如果不特別聲明的話,默認使用Atom協議,也就是Xml格式)。
和客戶端對象模型相同的是,我們通過Url,可以定義所需要返回的那些字段值,從而減少客戶端和服務器端的數據傳遞大小。
但是通過listdata這個接口名字也可以看出來,這個接口只提供列表數據的訪問能力。(在Excel Service中,也提供了類似的REST協議)
[注]如果需要在SharePoint 2010中使用ADO.NET Data Service,需要手動安裝.Net Framework 3.5 SP1 ADO.NET Data Service Update,這個更新包是不會包含在SharePoint 2010的預安裝組件里面的。
6、ADO.NET Data Service(強類型)
對於上述那個接口,如果我們使用的是Visual Studio開發后台代碼的話,還可以通過Visual Studio的連接到WCF服務的方式,由Visual Studio自動生成一個代理類。這樣也可以通過LINQ的方式去進行操作,就像是一個客戶端版本的LINQ to SharePoint。實際上,從下面的原理圖中我們可以看到,這種方式在服務器端,其實就是通過LINQ to SharePoint來完成的:
由於這種方式是綜合了REST協議(客戶端連接方式)和LINQ to SharePoint(服務器端處理方式),所以其優劣就是這兩者的綜合。
[注]如果需要更多關於ADO.NET Data Service的相關使用,可以參考MSDN上的這篇文章:Using the REST Interface
7、總結
通過表格的形式,對上述6種方式做一個比較:
| 服務器端效率 |
客戶端效率 |
對象是否強類型 |
數據是否強類型 |
涵蓋功能 |
開發難度 |
|
| 服務器端對象模型 |
高 |
- |
是 |
否 |
最完整 |
一般 |
| LINQ to SharePoint |
一般 |
- |
是 |
是 |
僅列表數據 |
容易 |
| Web Service |
高 |
低 |
否 |
否 |
較完整 |
復雜 |
| 客戶端對象模型 |
高 |
高 |
是 |
否 |
僅Foundation功能 |
一般 |
| REST |
高 |
高 |
否 |
否 |
僅列表數據 |
復雜 |
| 強類型ADO.NET Data Service |
一般 |
高 |
是 |
是 |
僅列表數據 |
容易 |
於是,當我們選擇數據訪問方式的時候,就可以得出下面的這種選擇路線(一家之言,歡迎討論):







