<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    飛艷小屋

    程序--人生--哲學___________________歡迎艷兒的加入

    BlogJava 首頁 新隨筆 聯系 聚合 管理
      52 Posts :: 175 Stories :: 107 Comments :: 0 Trackbacks
    編寫高性能 Web 應用程序的 10 個技巧
    編寫高性能 Web 應用程序的 10 個技巧
    發布日期: 2/1/2005 | 更新日期: 2/1/2005

    本文討論

    ?

    常見 ASP.NET 性能難點

    ?

    面向 ASP.NET 的有用性能提示和技巧

    ?

    在 ASP.NET 中使用數據庫的建議

    ?

    使用 ASP.NET 進行緩存和后臺處理

    本文使用了以下技術:
    ASP.NET、.NET Framework 和 IIS

    *
    本頁內容

    數據層性能 數據層性能 技巧 1 — 返回多個結果集 技巧 1 — 返回多個結果集 技巧 2 — 分頁的數據訪問 技巧 2 — 分頁的數據訪問 技巧 3 — 連接池 技巧 3 — 連接池 技巧 4 — ASP.NET 緩存 API 技巧 4 — ASP.NET 緩存 API 技巧 5 — 每請求緩存 技巧 5 — 每請求緩存 技巧 6 — 后臺處理 技巧 6 — 后臺處理 技巧 7 — 頁輸出緩存和代理服務器 技巧 7 — 頁輸出緩存和代理服務器 技巧 8 — 運行 IIS 6.0(只要用于內核緩存) 技巧 8 — 運行 IIS 6.0(只要用于內核緩存) 技巧 9 — 使用 Gzip 壓縮 技巧 9 — 使用 Gzip 壓縮 技巧 10 — 服務器控件視圖狀態 技巧 10 — 服務器控件視圖狀態 小結 小結

    使用 ASP.NET 編寫 Web 應用程序的簡單程度令人不敢相信。正因為如此簡單,所以很多開發人員就不會花時間來設計其應用程序的結構,以獲得更好的性能了。在本文中,我將講述 10 個用于編寫高性能 Web 應用程序的技巧。但是我并不會將這些建議僅局限于 ASP.NET 應用程序,因為這些應用程序只是 Web 應用程序的一部分。本文不作為對 Web 應用程序進行性能調整的權威性指南 — 一整本書恐怕都無法輕松講清楚這個問題。請將本文視作一個很好的起點。

    成為工作狂之前,我原來喜歡攀巖。在進行任何大型攀巖活動之前,我都會首先仔細查看指南中的路線,閱讀以前游客提出的建議。但是,無論指南怎么好,您都需要真正的攀巖體驗,然后才能嘗試一個特別具有挑戰性的攀登。與之相似,當您面臨修復性能問題或者運行一個高吞吐量站點的問題時,您只能學習如何編寫高性能 Web 應用程序。

    我的個人體驗來自在 Microsoft 的 ASP.NET 部門作為基礎架構程序經理的經驗,在此期間我運行和管理 www.ASP.NET,幫助設計社區服務器的結構,社區服務器是幾個著名 ASP.NET 應用程序(組合到一個平臺的 ASP.NET Forums、.Text 和 nGallery)。我確信有些曾經幫助過我的技巧對您肯定也會有所幫助。

    您應該考慮將應用程序分為幾個邏輯層。您可能聽說過 3 層(或者 n 層)物理體系結構一詞。這些通常都是規定好的體系結構方式,將功能在進程和/或硬件之間進行了物理分離。當系統需要擴大時,可以很輕松地添加更多的硬件。但是會出現一個與進程和機器跳躍相關的性能下降,因此應該避免。所以,如果可能的話,請盡量在同一個應用程序中一起運行 ASP.NET 頁及其相關組件。

    因為代碼分離以及層之間的邊界,所以使用 Web 服務或遠程處理將會使得性能下降 20% 甚至更多。

    數據層有點與眾不同,因為通常情況下,最好具有專用于數據庫的硬件。然而進程跳躍到數據庫的成本依然很高,因此數據層的性能是您在優化代碼時首先要考慮的問題。

    在深入應用程序的性能修復問題之前,請首先確保對應用程序進行剖析,以便找出具體的問題所在。主要性能計數器(如表示執行垃圾回收所需時間百分比的計數器)對于找出應用程序在哪些位置花費了其主要時間也非常有用。然而花費時間的位置通常非常不直觀。

    本文講述了兩種類型的性能改善:大型優化(如使用 ASP.NET 緩存),和進行自身重復的小型優化。這些小型優化有時特別有意思。您對代碼進行一點小小的更改,就會獲得很多很多時間。使用大型優化,您可能會看到整體性能的較大飛躍。而使用小型優化時,對于某個特定請求可能只會節省幾毫秒的時間,但是每天所有請求加起來,則可能會產生巨大的改善。

    數據層性能

    談到應用程序的性能調整,有一個試紙性的測試可用來對工作進行優先級劃分:代碼是否訪問數據庫?如果是,頻率是怎樣的?請注意,這一相同測試也可應用于使用 Web 服務或遠程處理的代碼,但是本文對這些內容未做講述。

    如果某個特定的代碼路徑中必需進行數據庫請求,并且您認為要首先優化其他領域(如字符串操作),則請停止,然后執行這個試紙性測試。如果您的性能問題不是非常嚴重的話,最好花一些時間來優化一下與數據庫、返回的數據量、進出數據庫的往返頻率相關的花費時間。

    了解這些常規信息之后,我們來看一下可能會有助于提高應用程序性能的十個技巧。首先,我要講述可能會引起最大改觀的更改。

    技巧 1 — 返回多個結果集

    仔細查看您的數據庫代碼,看是否存在多次進入數據庫的請求路徑。每個這樣的往返都會降低應用程序可以提供的每秒請求數量。通過在一個數據庫請求中返回多個結果集,可以節省與數據庫進行通信所需的總時間長度。同時因為減少了數據庫服務器管理請求的工作,還會使得系統伸縮性更強。

    雖然可以使用動態 SQL 返回多個結果集,但是我首選使用存儲過程。關于業務邏輯是否應該駐留于存儲過程的問題還存在一些爭議,但是我認為,如果存儲過程中的邏輯可以約束返回數據的話(縮小數據集的大小、縮短網絡上所花費時間,不必篩選邏輯層的數據),則應贊成這樣做。

    使用 SqlCommand 實例及其 ExecuteReader 方法填充強類型的業務類時,可以通過調用 NextResult 將結果集指針向前移動。圖 1 顯示了使用類型類填充幾個 ArrayList 的示例會話。只從數據庫返回您需要的數據將進一步減少服務器上的內存分配。

    技巧 2 — 分頁的數據訪問

    ASP.NET DataGrid 具有一個很好的功能:數據分頁支持。在 DataGrid 中啟用分頁時,一次會顯示固定數量的記錄。另外,在 DataGrid 的底部還會顯示分頁 UI,以便在記錄之間進行導航。該分頁 UI 使您能夠在所顯示的數據之間向前和向后導航,并且一次顯示固定數量的記錄。

    還有一個小小的波折。使用 DataGrid 的分頁需要所有數據均與網格進行綁定。例如,您的數據層需要返回所有數據,那么 DataGrid 就會基于當前頁篩選顯示的所有記錄。如果通過 DataGrid 進行分頁時返回了 100,000 個記錄,那么針對每個請求會放棄 99,975 個記錄(假設每頁大小為 25 個記錄)。當記錄的數量不斷增加時,應用程序的性能就會受到影響,因為針對每個請求必須發送越來越多的數據。

    要編寫性能更好的分頁代碼,一個極佳的方式是使用存儲過程。圖 2 顯示了針對 Northwind 數據庫中的 Orders 表進行分頁的一個示例存儲過程。簡而言之,您此時要做的只是傳遞頁索引和頁大小。然后就會計算合適的結果集,并將其返回。

    在社區服務器中,我們編寫了一個分頁服務器控件,以完成所有的數據分頁。您將會看到,我使用的就是技巧 1 中討論的理念,從一個存儲過程返回兩個結果集:記錄的總數和請求的數據。

    返回記錄的總數可能會根據所執行查詢的不同而有所變化。例如,WHERE 子句可用來約束返回的數據。為了計算在分頁 UI 中顯示的總頁數,必須了解要返回記錄的總數。例如,如果總共有 1,000,000 條記錄,并且要使用一個 WHERE 子句將其篩選為 1000 條記錄,那么分頁邏輯就需要了解記錄的總數才能正確呈現分頁 UI。

    技巧 3 — 連接池

    在 Web 應用程序和 SQL Server? 之間設置 TCP 連接可能是一個非常消耗資源的操作。Microsoft 的開發人員到目前為止能夠使用連接池已經有一段時間了,這使得他們能夠重用數據庫連接。他們不是針對每個請求都設置一個新的 TCP 連接,而是只在連接池中沒有任何連接時才設置新連接。當連接關閉時,它會返回連接池,在其中它會保持與數據庫的連接,而不是完全破壞該 TCP 連接。

    當然,您需要小心是否會出現泄漏連接。當您完成使用連接時,請一定要關閉這些連接。再重復一遍:無論任何人對 Microsoft?.NET Framework 中的垃圾回收有什么評論,請一定要在完成使用連接時針對該連接顯式調用 Close 或 Dispose。不要相信公共語言運行庫 (CLR) 會在預先確定的時間為您清除和關閉連接。盡管 CLR 最終會破壞該類,并強制連接關閉,但是當針對對象的垃圾回收真正發生時,并不能保證。

    要以最優化的方式使用連接池,需要遵守一些規則。首先打開連接,執行操作,然后關閉該連接。如果您必須如此的話,可以針對每個請求多次打開和關閉連接(最好應用技巧 1),但是不要一直將連接保持打開狀態并使用各種不同的方法對其進行進出傳遞。第二,使用相同的連接字符串(如果使用集成身份驗證的話,還要使用相同的線程標識)。如果不使用相同的連接字符串,例如根據登錄的用戶自定義連接字符串,那么您將無法得到連接池提供的同一個優化值。如果您使用集成身份驗證,同時還要模擬大量用戶,連接池的效率也會大大下降。嘗試跟蹤與連接池相關的任何性能問題時,.NET CLR 數據性能計數器可能非常有用。

    每當應用程序連接資源時,如在另一個進程中運行的數據庫,您都應該重點考慮連接該資源所花時間、發送或檢索數據所花時間,以及往返的數量,從而進行優化。優化應用程序中任何種類的進程跳躍都是獲得更佳性能的首要一點。

    應用層包含了連接數據層、將數據轉換為有意義類實例和業務流程的邏輯。例如社區服務器,您要在其中填充Forums 或 Threads集合,應用業務規則(如權限);最重要的是要在其中執行緩存邏輯。

    技巧 4 — ASP.NET 緩存 API

    編寫應用程序代碼行之前,一個首要完成的操作是設計應用層的結構,以便最大化利用 ASP.NET 緩存功能。

    如果您的組件要在 ASP.NET 應用程序中運行,則只需在該應用程序項目中包括一個 System.Web.dll 引用。當您需要訪問該緩存時,請使用 HttpRuntime.Cache 屬性(通過 Page.Cache 和 HttpContext.Cache 也可訪問這個對象)。

    對于緩存數據,有幾個規則。首先,如果數據可能會多次使用時,則這是使用緩存的一個很好的備選情況。第二,如果數據是通用的,而不特定于某個具體的請求或用戶時,則也是使用緩存的一個很好的備選情況。如果數據是特定于用戶或請求的,但是壽命較長的話,仍然可以對其進行緩存,但是這種情況可能并不經常使用。第三,一個經常被忽略的規則是,有時可能您緩存得太多。通常在一個 x86 計算機上,為了減少內存不足錯誤出現的機會,您會想使用不高于 800MB 的專用字節運行進程。因此緩存應該有個限度。換句話說,您可能能夠重用某個計算結果,但是如果該計算采用 10 個參數的話,您可能要嘗試緩存 10 個排列,這樣有可能給您帶來麻煩。一個要求 ASP.NET 的最常見支持是由于過度緩存引起的內存不足錯誤,尤其是對于大型數據集。


    圖 3 ASP.NET緩存


    緩存有幾個極佳的功能,您需要對它們有所了解。首先,緩存會實現最近最少使用的算法,使得 ASP.NET 能夠在內存運行效率較低的情況下強制緩存清除 - 從緩存自動刪除未使用過的項目。第二,緩存支持可以強制失效的過期依賴項。這些依賴項包括時間、密鑰和文件。時間經常會用到,但是對于 ASP.NET 2.0,引入了一個功能更強的新失效類型:數據庫緩存失效。它指的是當數據庫中的數據發生變化時自動刪除緩存中的項。有關數據庫緩存失效的詳細信息,請參閱 MSDN?Magazine 2004 年 7 月的 Dino Esposito Cutting Edge 專欄。要了解緩存的體系結構,請參閱圖 3。

    技巧 5 — 每請求緩存

    在本文前面部分,我提到了經常遍歷代碼路徑的一些小改善可能會導致較大的整體性能收益。對于這些小改善,其中有一個絕對是我的最愛,我將其稱之為“每請求緩存”。

    緩存 API 的設計目的是為了將數據緩存較長的一段時間,或者緩存至滿足某些條件時,但每請求緩存則意味著只將數據緩存為該請求的持續時間。對于每個請求,要經常訪問某個特定的代碼路徑,但是數據卻只需提取、應用、修改或更新一次。這聽起來有些理論化,那么我們來舉一個具體的示例。

    在社區服務器的論壇應用程序中,頁面上使用的每個服務器控件都需要個性化的數據來確定使用什么外觀、使用什么樣式表,以及其他個性化數據。這些數據中有些可以長期緩存,但是有些數據卻只針對每個請求提取一次,然后在執行該請求期間對其重用多次,如要用于控件的外觀。

    為了達到每請求緩存,請使用 ASP.NET HttpContext。對于每個請求,都會創建一個 HttpContext 實例,在該請求期間從 HttpContext.Current 屬性的任何位置都可訪問該實例。該 HttpContext 類具有一個特殊的 Items 集合屬性;添加到此 Items 集合的對象和數據只在該請求持續期間內進行緩存。正如您可以使用緩存來存儲經常訪問的數據一樣,您也可以使用 HttpContext.Items 來存儲只基于每個請求使用的數據。它背后的邏輯非常簡單:數據在它不存在的時候添加到 HttpContext.Items 集合,在后來的查找中,只是返回 HttpContext.Items 中的數據。

    技巧 6 — 后臺處理

    通往代碼的路徑應該盡可能快速,是嗎?可能有時您會覺得針對每個請求執行的或者每 n 個請求執行一次的任務所需資源非常多。發送電子郵件或者分析和驗證傳入數據就是這樣的一些例子。

    剖析 ASP.NET Forums 1.0 并重新構建組成社區服務器的內容時,我們發現添加新張貼的代碼路徑非常慢。每次添加新張貼時,應用程序首先需要確保沒有重復的張貼,然后必須使用“壞詞”篩選器分析該張貼,分析張貼的字符圖釋,對張貼添加標記并進行索引,請求時將張貼添加到合適的隊列,驗證附件,最終張貼之后,立即向所有訂閱者發出電子郵件通知。很清楚,這涉及很多操作。

    經研究發現,大多數時間都花在了索引邏輯和發送電子郵件上。對張貼進行索引是一個非常耗時的操作,人們發現內置的 System.Web.Mail 功能要連接 SMYP 服務器,然后連續發送電子郵件。當某個特定張貼或主題領域的訂閱者數量增加時,執行 AddPost 功能所需的時間也越來越長。

    并不需要針對每個請求都進行電子郵件索引。理想情況下,我們想要將此操作進行批處理,一次索引 25 個張貼或者每五分鐘發送一次所有電子郵件。我們決定使用以前用于對數據緩存失效進行原型設計的代碼,這個失效是用于最終進入 Visual Studio? 2005 的內容的。

    System.Threading 命名空間中的 Timer 類非常有用,但是在 .NET Framework 中不是很有名,至少對于 Web 開發人員來說是這樣。創建之后,這個 Timer 類將以一個可配置的間隔針對 ThreadPool 中的某個線程調用指定的回調。這就表示,您可以對代碼進行設置,使其能夠在沒有對 ASP.NET 應用程序進行傳入請求的情況下得以執行,這是后臺處理的理想情況。您還可以在此后臺進程中執行如索引或發送電子郵件之類的操作。

    但是,這一技術有幾個問題。如果應用程序域卸載,該計時器實例將停止觸發其事件。另外,因為 CLR 對于每個進程的線程數量具有一個硬性標準,所以可能會出現這樣的情形:服務器負載很重,其中計時器可能沒有可在其基礎上得以完成的線程,在某種程度上可能會造成延遲。ASP.NET 通過在進程中保留一定數量的可用線程,并且僅使用總線程的一部分用于請求處理,試圖將上述情況發生的機會降到最低。但是,如果您具有很多異步操作時,這可能就是一個問題了。

    這里沒有足夠的空間來放置該代碼,但是您可以下載一個可以看懂的示例,網址是 www.rob-howard.net。請了解一下 Blackbelt TechEd 2004 演示中的幻燈片和演示。

    技巧 7 — 頁輸出緩存和代理服務器

    ASP.NET 是您的表示層(或者說應該是您的表示層);它由頁、用戶控件、服務器控件(HttpHandlers 和 HttpModules)以及它們生成的內容組成。如果您具有一個 ASP.NET 頁,它會生成輸出(HTML、XML、圖像或任何其他數據),并且您針對每個請求運行此代碼時,它都會生成相同的輸出,那么您就擁有一個可用于頁輸出緩存的絕佳備選內容。

    將此行內容添加頁的最上端

    <%@ Page OutputCache VaryByParams="none" Duration="60" %>

    就可以高效地為此頁生成一次輸出,然后對它進行多次重用,時間最長為 60 秒,此時該頁將重新執行,輸出也將再一次添加到 ASP.NET 緩存。通過使用一些低級程序化 API 也可以完成此行為。對于輸出緩存有幾個可配置的設置,如剛剛講到的 VaryByParams 屬性。VaryByParams 剛好被請求到,但還允許您指定 HTTP GET 或 HTTP POST 參數來更改緩存項。例如,只需設置 VaryByParam="Report" 即可對 default.aspx?Report=1 或 default.aspx?Report=2 進行輸出緩存。通過指定一個以分號分隔的列表,還可以指定其他參數。

    很多人都不知道何時使用輸出緩存,ASP.NET 頁還會生成一些位于緩存服務器下游的 HTTP 標頭,如 Microsoft Internet Security and Acceleration Server 或 Akamai 使用的標頭。設置了 HTTP 緩存標頭之后,可以在這些網絡資源上對文檔進行緩存,客戶端請求也可在不必返回原始服務器的情況下得以滿足。

    因此,使用頁輸出緩存不會使得您的應用程序效率更高,但是它可能會減少服務器上的負載,因為下游緩存技術會緩存文檔。當然,這可能只是匿名內容;一旦它成為下游之后,您就再也不會看到這些請求,并且再也無法執行身份驗證以阻止對它的訪問了。

    技巧 8 — 運行 IIS 6.0(只要用于內核緩存)

    如果您未運行 IIS 6.0 (Windows Server? 2003),那么您就錯過了 Microsoft Web 服務器中的一些很好的性能增強。在技巧 7 中,我討論了輸出緩存。在 IIS 5.0 中,請求是通過 IIS 然后進入 ASP.NET 的。涉及到緩存時,ASP.NET 中的 HttpModule 會接收該請求,并返回緩存中的內容。

    如果您正在使用 IIS 6.0,就會發現一個很好的小功能,稱為內核緩存,它不需要對 ASP.NET 進行任何代碼更改。當請求由 ASP.NET 進行輸出緩存時,IIS 內核緩存會接收緩存數據的一個副本。當請求來自網絡驅動程序時,內核級別的驅動程序(無上下文切換到用戶模式)就會接收該請求,如果經過了緩存,則會將緩存的數據刷新到響應,然后完成執行。這就表示,當您將內核模式緩存與 IIS 和 ASP.NET 輸出緩存一起使用時,就會看到令人不敢相信的性能結果。在 ASP.NET 的 Visual Studio 2005 開發過程中,我一度是負責 ASP.NET 性能的程序經理。開發人員完成具體工作,但是我要看到每天進行的所有報告。內核模式緩存結果總是最有意思的。最常見的特征是網絡充滿了請求/響應,而 IIS 運行時的 CPU 使用率只有大約 5%。這太令人震驚了!當然使用 IIS 6.0 還有一些其他原因,但是內核模式緩存是其中最明顯的一個。

    技巧 9 — 使用 Gzip 壓縮

    雖然使用 gzip 并不一定是服務器性能技巧(因為您可能會看到 CPU 使用率的提高),但是使用 gzip 壓縮可以減少服務器發送的字節數量。這就使人們覺得頁速度加快了,并且還減少了帶寬的用量。根據所發送數據、可以壓縮的程度以及客戶端瀏覽器是否支持(IIS 只會向支持 gzip 壓縮的客戶端發送經過 gzip 壓縮的內容,如 Internet Explorer 6.0 和 Firefox),您的服務器每秒可以服務于更多的請求。實際上,幾乎每當您減少所返回數據的數量時,都會增加每秒請求數。

    Gzip 壓縮已經內置到 IIS 6.0 中,并且其性能比 IIS 5.0 中使用的 gzip 壓縮要好的多,這是好消息。但不幸的是,當嘗試在 IIS 6.0 中打開 gzip 壓縮時,您可能無法在 IIS 的屬性對話中找到該設置。IIS 小組在該服務器中置入了卓越的 gzip 功能,但是忘了包括一個用于啟用該功能的管理 UI。要啟用 gzip 壓縮,您必須深入到 IIS 6.0 的 XML 配置設置內部(這樣不會引起心臟虛弱)。順便提一句,這歸功于 OrcsWeb 的 Scott Forsyth,他幫助我提出了在 OrcsWeb 上宿主的 www.asp.net 服務器的這個問題。

    本文就不講述步驟了,請閱讀 Brad Wilson 的文章,網址是 IIS6 Compression。還有一篇有關為 ASPX 啟用壓縮的知識庫文章,網址是 Enable ASPX Compression in IIS。但是您應該注意,由于一些實施細節,IIS 6.0 中不能同時存在動態壓縮和內核緩存。

    技巧 10 — 服務器控件視圖狀態

    視圖狀態是一個有趣的名稱,用于表示在所生成頁的隱藏輸出字段中存儲一些狀態數據的 ASP.NET。當該頁張貼回服務器時,服務器可以分析、驗證、并將此視圖狀態數據應用回該頁的控件樹。視圖狀態是一個非常強大的功能,因為它允許狀態與客戶端一起保持,并且它不需要 cookie 或服務器內存即可保存此狀態。很多 ASP.NET 服務器控件都使用視圖狀態來保持在與頁元素進行交互期間創建的設置,例如保存對數據進行分頁時顯示的當前頁。

    然而使用視圖狀態也有一些缺點。首先,服務或請求頁時,它都會增加頁的總負載。對張貼回服務器的視圖狀態數據進行序列化或取消序列化時,也會發生額外的開銷。最后,視圖狀態會增加服務器上的內存分配。

    幾個服務器控件有著過度使用視圖狀態的趨勢,即使在并不需要的情況下也要使用它,其中最著名的是 DataGrid。ViewState 屬性的默認行為是啟用,但是如果您不需要,則可以在控件或頁級別關閉。在控件內,只需將 EnableViewState 屬性設置為 false,或者在頁中使用下列設置即可對其進行全局設置:

    <%@ Page EnableViewState="false" %>

    如果您不回發頁,或者總是針對每個請求重新生成頁上的控件,則應該在頁級別禁用視圖狀態。

    小結

    我為您講述了一些我認為在編寫高性能 ASP.NET 應用程序時有所幫助的技巧。正如我在本文前面部分提到的那樣,這是一個初步指南,并不是 ASP.NET 性能的最后結果。(有關改善 ASP.NET 應用程序性能的信息,請參閱 Improving ASP.NET Performance。)只有通過自己的親身體驗才能找出解決具體性能問題的最好方法。但是,在您的旅程中,這些技巧應該會為您提供一些好的指南。在軟件開發中,幾乎沒有絕對的東西;每個應用程序都是唯一的。

    請參閱提要欄“Common Performance Myths”。

    Rob Howard 是 Telligent Systems 的創始人,專門從事高性能 Web 應用程序、知識庫管理和協作系統方面的工作。Rob 以前受雇于 Microsoft,他在那里幫助設計了 ASP.NET 1.0、1.1 和 2.0 的基礎結構。要聯系 Rob,請訪問 rhoward@telligentsystems.com

    16:29  |  固定鏈接 | 評論 (0) | 引用通告 (0) | 記錄它
    用C#生成Excel文件的方法和Excel.dll組件生成的方法
    標題   用C#生成Excel文件的方法和Excel.dll組件生成的方法     選擇自 wang8712 的 Blog 關鍵字   用C#生成Excel文件的方法和Excel.dll組件生成的方法 出處  

    一個示例:

    class AppTest
     {
      private Excel.ApplicationClass _x;
      public static void Main0()
      {
       AppTest a = new AppTest();
       a._x = new Excel.ApplicationClass();
       a._x.UserControl = false;
       for (int i = 0 ;i < 4; i++)
       {
        
        a.SaveToXls("D:\\test\\" + i + ".xls");  // 本例是在D盤下建立的test文件夾
       }
       a._x.Quit();
       System.Runtime.InteropServices.Marshal.ReleaseComObject((object) a._x);
       System.GC.Collect();
      }

      private void SaveToXls(string filename)
      {
       Excel.WorkbookClass wb = (Excel.WorkbookClass) this._x.Workbooks.Add(System.Reflection.Missing.Value);
       for(int i = 1;i <= 4; i++)
       {
        this._x.Cells[i,1]=i.ToString();
        this._x.Cells[i,2]="'bbb2";
        this._x.Cells[i,3]="'ccc3";
        this._x.Cells[i,4]="'aaa4";
       }
       
       wb.Saved = true;
       this._x.ActiveWorkbook.SaveCopyAs(filename);
      }
     }

    【注:在VS.Net中運行是要添加Excel.dll組件的,Excel組件VS.Net本身是沒有的,下面是生成Excel.dll的方法。】

    1.要保證機器本身要安裝OFFICE.

    2.把[C:\Program Files\Microsoft Office\Office:默認安裝路徑]下的EXCEL9.OLB文件拷貝到[C:\Visual Studio.Net\SDK\v1.1\Bin:VS.Net安裝路徑]路徑下。

    3.打開Visual Studio .Net2003命令提示,運行TlbImp Excel9.olb Excel.dll ,就會在[C:\Visual Studio.Net\SDK\v1.1\Bin]下生成Excel.dll組件。

    4.在項目中添加Excel.dll引用就OK了。

    13:53  |  固定鏈接 | 評論 (0) | 引用通告 (0) | 記錄它
    利用 SQL Server Reporting Services 從應用程序生成用戶友好的報表
    http://www.microsoft.com/china/msdn/library/data/sqlserver/SQLServerReportServ.mspx 利用 SQL Server Reporting Services 從應用程序生成用戶友好的報表
    發布日期: 09/03/2004 | 更新日期: 09/03/2004
    本文討論: ?

    Reporting

    ?

    設計和部署報表

    ?

    使用 Reporting Services 的 Web 服務

    ?

    保證報表的安全

    本文使用下列技術:
    SQL Server、ASP.NET、Visual Basic .NET

    代碼下載:
    SQLServerReportingServices.exe(222KB)

    *
    本頁內容

    Reporting Services 概述 Reporting Services 概述 設計第一個報表 設計第一個報表 部署和測試 部署和測試 將報表添加到 Web 應用程序 將報表添加到 Web 應用程序 將報表嵌入到 Web 應用程序中 將報表嵌入到 Web 應用程序中 保證報表的安全 保證報表的安全 使用 Reporting Services Web 服務 使用 Reporting Services Web 服務 訂閱報表 訂閱報表 接下來該怎么做? 接下來該怎么做?

    靈活的報表功能是大多數業務應用程序的一個要求,這些報表功能在集成到 Web 應用程序中之后用途更加廣泛。利用 SQL Server? 2000 Reporting Services 的最新版本,您可以輕松地具有來自各種數據源的報表生成功能。在本文中,我將介紹使用 Visual Studio? 和 Reporting Services 來編寫報表,并演示如何將報表集成到 Web 應用程序中。


    Reporting Services 是基于服務器的報表生成平臺,該平臺構建在 .NET Framework 上并與 SQL Server 2000 集成在一起,因此您可以使用一個擴展的基于 Web 服務的 API 將豐富的報表生成功能集成到應用程序中。盡管報表服務器使用 SQL Server 作為報表的儲存庫,但利用 OLE DB、ODBC 或 ADO.NET 提供程序的任何數據源都可以用來為報表提供數據,這就使得 Reporting Services 成為在各種企業環境中生成報表的極佳工具。


    Reporting Services 是作為 SQL Server 2000 的一部分而授權的,因此如果您有已授權的 SQL Server 的副本,則可以在同一服務器上運行 Reporting Services 而無需支付額外的許可證費用。如果您決定在獨立的、尚未經 SQL Server 授權的計算機(例如,Web 服務器)上運行 Reporting Services,您將需要購買額外的 SQL Server 許可證。


    要開始使用 Reporting Services,您可以下載位于 SQL Server 2000 Reporting Services 的 120 天試用版軟件,它還包括有關如何獲得該產品的安裝媒體的說明。在安裝該產品時,請確保選中包括示例報表的選項,因為我要將這些報表集成到本文的 Web 應用程序中。



    Reporting Services 概述

    Reporting Services 報表設計使用新的 Visual Studio .NET 2003 報表設計器,在安裝有 Visual Studio 的同一計算機上安裝了 Reporting Services 后,該設計器就可以使用。設計器以新的基于 XML 報表定義語言 (RDL) 輸出報表布局和數據訪問的說明,而 RDL 文件會發布到報表服務器。如果您希望為用戶提供創作功能,也可以使用第三方的報表設計器。


    管理員可以使用基于 Web 的報表管理器來管理已發布的報表,并且可以執行一些任務,例如,保證某些用戶組報表的安全或更改已部署報表的數據源連接字符串。用戶也可以使用報表管理器來瀏覽和查看報表;但是很多公司傾向于使用 URL 請求或 Reporting Services Web 服務,將報表查看直接集成到它們現有的內部 Web 站點或應用程序中。



    USissues0408SQLServerReportingServicesfig01

    1 Reporting Services 體系結構



    可以將報表參數化,這樣用戶可以在查看報表時從選擇列表中進行挑選;它們還可以導出為多種格式,例如,Microsoft?Excel、PDF 和 XML。盡管實時報表提供最新的數據,也可以將報表緩存一段時間以提高性能并減少數據源上的負載。對于商務智能應用程序,報表可以訪問 Analysis Services OLAP 多維數據集,而且 Reporting Services 甚至可以導入現有的 Microsoft Access 報表,盡管由于這兩種技術間存在差異,它并不能支持所有的 Access 功能。有關 Reporting Services 體系結構的詳細情況,請參見 1



    設計第一個報表

    盡管我介紹的重點在于將報表集成到應用程序中而不是創作報表,但熟悉 Reporting Services 的最好辦法就是立即著手創建一個新的報表項目。為此,請打開 Visual Studio 并使用 Report Project Wizard 創建一個新的項目,該向導位于 Business Intelligence Projects 部分。為您的項目指定一個名稱,單擊“OK”,然后將顯示報表向導對話框。在您選擇數據源的這一步驟中,單擊“Edit”按鈕,以便指定連接到您數據庫服務器上的 AdventureWorks2000,單擊“OK”,然后選中使其成為共享數據源的選項。


    單擊“Next”進入到“Design the Query”步驟,指定簡單的 SQL 語句(例如,“SELECT * FROM Product”),然后單擊“Next”。Reporting Services 支持顯示一組固定列的標準表格式 (Tabular) 報表,也支持使用帶有動態列的交叉表樣式視圖的矩陣式 (Matrix) 報表,因此為該報表選擇 Tabular,然后單擊“Next”。


    將某些列(例如,ProductID 和 Name)拖到 Details 部分并單擊“Next”。選擇默認的 Bold 樣式,單擊“Next”,然后指定您的報表服務器的虛擬目錄的 URL,例如,http://MyServer/ReportServer。現在,單擊“Next”。最后一步,您可以為您的第一個報表指定名稱,例如,Products Report;然后單擊“Finish”。


    該向導將會創建該項目,同時還創建一個可以由多個報表共享 AdventureWorks2000 數據源,以及一個在設計器中打開的有關 Products 數據的報表(請參見 2)。默認情況下,顯示使您可以修改報表設計的 Layout 選項卡。



    USissues0408SQLServerReportingServicesfig02

    2 Visual Studio 報表設計器




    Data 選項卡用于指定報表的查詢,包括設置參數以便在執行報表時提示用戶提供值。單擊“Preview”選項卡來測試報表顯示給用戶的方式。


    現在,您可以將附加的報表添加到該項目,或者通過添加附加列、將數據分組、添加匯總、甚至是添加能夠顯示來自其他查詢的數據的附加表來修改 Products 報表。與很多報表設計器不同,Reporting Services 使用帶外設計,這使得它可以輕松地創建將各種源的數據集中在同一個位置的報表。


    您剛剛創建的報表項目由當前駐留在您的開發計算機上的一組文件組成。為了將這些報表發布給用戶,您需要將該項目部署到報表服務器。



    部署和測試

    在部署報表項目之前,需要指定將項目部署到哪一個服務器。如果您最初使用該向導創建了報表項目,那么您可能已經指定了目標服務器。


    要檢查該設置或指定一個新服務器,請選擇“Project | Properties”來顯示 Project Properties 對話框。進行該設置使您可以指定項目的文件夾名和 Reporting Services Web 服務的 URL。在菜單上選擇“Build | Deploy Solution”將構建報表項目,然后將其部署到服務器。


    在部署報表項目后,您就可以進行測試了。已部署到服務器的每個報表都具有唯一的路徑,該路徑可以用于在瀏覽器中顯示報表。例如,要顯示您創建的第一個報表,請打開瀏覽器并導航到 http://MyServer/ReportServer?/Report+Project1/Products+Report。這會以 HTML 格式呈現報表并在頁面的頂端顯示一個工具欄,以便使您可以對報表進行翻頁、更改縮放比例,甚至以各種其他格式(例如,PDF)查看報表。


    您還可以通過導航到服務器上的以下 URL,來瀏覽已部署到服務器的所有報表項目的列表:http://MyServer/ReportServer。報表服務器提供使您可以查看已部署的報表類型和查看報表的最小的用戶界面。


    報表管理器是一個更加友好、功能更豐富的工具,可以通過簡單地導航到本地服務器上的以下 URL 來訪問該工具:http://MyServer/Reports

    將報表添加到 Web 應用程序

    既然您知道如何設計、部署和測試報表,就讓我們將這些報表集成到應用程序中吧!很多 Web 應用程序都包括針對每個用戶的自定義主頁,通常稱為“儀表板”。這個頁面通常提供針對用戶量身定做的摘要式信息并作為跳轉點來訪問應用程序的其他功能。


    我將使用 Reporting Services 附帶的示例報表構建一個儀表板來演示報表集成,如圖 3 所示。如果您在安裝過程中安裝了示例報表,您可以打開該報表項目(安裝到 \Reporting Services\Samples\Reports)并將其部署到測試服務器。您可以使用報表管理器工具通過瀏覽到 SampleReports 文件夾來測試示例報表。



    USissues0408SQLServerReportingServicesfig03

    3 儀表板 Web 應用程序




    為用戶提供從應用程序訪問報表的權限的最簡單方法就是使用超級鏈接。如您已經看到的那樣,每個報表都具有唯一的、用于在瀏覽器中顯示報表的路徑。對于儀表板應用程序,報表應該在單獨的窗口中打開,因此我將為該超級鏈接指定一個目標。到目前為止,這是非常簡單的,但是對于具有參數報表的報表(例如,Employee Sales Summary 報表)來說,情況又如何呢?要真正地將報表集成到應用程序中,您通常需要為參數指定某些值,以便用戶不會被經常提示。


    Reporting Services 使這項任務非常簡單:報表參數的值可以指定為 URL 的一部分。例如,Employee Sales Summary 報表具有 ReportYear、ReportMonth 和 EmpID 參數,因此顯示編號為 24 的員工在 11 月份的銷售額的超級鏈接可以是:

    <a href="http://MyServer/ReportServer?/SampleReports/Employee Sales Summary&ReportMonth=11&EmpID=24" target="_blank">November Sales Summary </a>

    不具有在 URL 中指定的值的參數將使用在報表設計器中設置的默認值,因此剛才展示的示例銷售匯總報表使用的 ReportYear 值是 2003。Reporting Services 還有一組用于控制報表顯示方式的內置參數。這些參數通過加上前綴“rs:”來區別您自己的報表參數。


    其中最有用的一個參數就是 rs:Format 參數,它用于指定呈現報表的格式。這使您可以包括不需要顯示在 HTML 中的報表,還可以使用諸如 PDF 甚至是 XML 的格式。其他常用參數是 rs:Command,指示出要應用到您所指定的路徑的操作。例如,rs:Command=Render 將呈現一個報表,而 rs:Command=ListChildren 將列出文件夾中所有項。


    如果您沒有指定命令,Reporting Services 將查看您指定的路徑并計算出要采取的適當操作,例如,呈現報表。圖 4 顯示了 rs:parameters 的列表。
    另一組內置參數控制各種輸出格式的行為并使用 rc:prefix。每個報表格式都具有其自己特定的一組參數。例如,要以沒有標題行的逗號分隔值 (CSV) 格式呈現報表,您應該使用 rc:NoHeader 參數:

    <a href="http://MyServer/ReportServer?/SampleReports/Company Sales &rs:Command=Render&rs:Format=CSV&rc:NoHeader=true">Company Sales </a>

    HTML 格式具有非常多的 rc:parameters,在將報表集成到 Web 應用程序中時為您提供很多的靈活性。如果應用程序將報表參數選擇傳遞到報表,那么您可能要通過將 rc:Parameters 設置為 false 來避免提示用戶,甚至可以通過將 rc:Toolbar 設置為 false 來關閉整個工具欄。有關常用 HTML rc:parameters 的列表的詳細信息,請參見圖 5


    如果您的 Web 應用程序使用框架來顯示報表(比如說,在左側窗格中顯示報表列表,在右側窗格中顯示報表內容),您將需要使用 rc:LinkTarget 參數來指定內容框架的名稱。否則,當用戶點擊報表中的任意嵌入鏈接時,瀏覽器就會使用整個窗口來重新顯示報表,而不是保留導航窗格和內容窗格的框架。


    您可以使用我介紹過的 URL 參數來添加“快速啟動”區域,該區域包含使用戶可以打開常用報表的超級鏈接的列表(請參見圖 3)。每個超級鏈接將其目標設置為 _blank 以在獨立的瀏覽器窗口中打開報表,并使用諸如 rs:Format=EXCEL 這樣的參數來控制報表在瀏覽器中的顯示方式。


    將報表嵌入到 Web 應用程序中

    如果在用戶查看報表時,不再使用彈出新的瀏覽器窗口,您可能希望實際地將報表嵌入到自己的 Web 頁中。最簡單的方法就是在 Web 頁上使用 IFRAME,并使用我介紹過的基于 URL 的相同技術來設置 SRC 屬性。如果您使用這種技術,切記要將 rc:LinkTarget 參數設置為 IFRAME 的名稱,以避免當用戶單擊報表的鏈接時在框架外彈出任意一個框架。


    如果您以前設計過 ASP.NET 的服務器控件,您可能會認為 IFRAME 技術并不十分完美,因為它要求 Web 開發人員理解 Reporting Services 的 URL 參數語法才能構建適當的 SRC 字符串。構建一個封裝有所有參數并使開發人員可以簡單地設置諸如 ReportPath 和 Zoom 之類的參數的服務器控件難道不是一個更好的想法嗎?


    Reporting Services 非常方便地附帶了名為 ReportViewer 的示例(安裝到 \Reporting Services\Samples\Applications\ReportViewer 目錄),它可以實現這個想法。該控件封裝了 IFRAME 和 URL 訪問參數邏輯,并提供了一個更簡單的用于將報表嵌入到應用程序中的方法。要使用該控件,請打開并構建 ReportViewer 解決方案,然后通過瀏覽到 ReportViewer 解決方案的 bin 目錄中的 ReportViewer.dll,切換到您的 Web 應用程序并將該控件添加到工具箱中。
    您可以使用該控件將嵌入報表添加到儀表板應用程序中。將控件添加到 Web 頁,設置高度和寬度屬性并添加以下代碼以便在頁面中顯示一個報表:

    Private Sub Page_Load(ByVal sender As System.Object, ByVal _ e As System.EventArgs) Handles MyBase.Load If Not IsPostBack Then ReportViewer1.ServerUrl = "http://MyServer/ReportServer" ReportViewer1.ReportPath = "/SampleReports/Sales Order" & _ "Detail&SalesOrderNumber=so8153" ReportViewer1.Toolbar = ReportViewer.multiState.False ReportViewer1.Zoom = "75" End If End Sub
    保證報表的安全

    Reporting Services 安全性使用熟悉的基于角色的模型。用戶和組可以分配給諸如 System Administrator 或 Browser 之類的角色,特定角色的所有成員都將允許執行為該角色定義的操作。


    報表管理器用于執行與安全性相關的任務,例如,分配用戶角色或修改項(例如,報表)的權限等。默認情況下,Administrators Windows 組同時是所有文件夾的 System Administrator 和 Content Manager 的成員。為了擴展報表訪問到其他用戶,用戶帳戶或組必須添加到諸如 Browser 的角色中。這通常是在文件夾級設置的,同時也可以針對單獨的報表進行重寫。


    除了保護對報表的訪問外,管理員還必須決定如何將報表連接到源數據庫。可以配置數據源使用集成的安全性,以便可以使用當前的用戶帳戶進行數據庫連接。此外,還可以定義 Windows? 或 SQL Server 用戶名和密碼,并將其安全地存儲在報表服務器數據庫中。這種方法要求報表運行在無用戶交互(例如,訂閱所涉及的交互)的情況下。


    使用 Reporting Services Web 服務

    到目前為止,我一直使用的 URL 訪問方法提供了一種快速便捷的方式來查看報表并指定參數,但是它不能提供對所有可用的報表管理功能的訪問。Reporting Services 還附帶了一個具有豐富功能的 API,為您提供通過 Web 服務對高級功能的訪問。例如,該 Web 服務包括用于管理報表的數據源信息的方法或者枚舉某個文件夾中所有項的方法。


    我將使用 Web 服務將報表的動態列表添加到儀表板應用程序中。為此,您將需要使用 Add Web Reference 將一個 Web 服務引用添加到應用程序中。指定到 Web 服務的路徑,如下所示:

    http://MyServer/ReportServer/ReportService.asmx

    在添加引用后,您可以創建 ReportingService 代理類的一個實例,并調用該服務上的一個方法,就像我在此處完成的一樣:

    Private Sub LoadSampleReports() Dim rs As New ReportingService rs.Credentials = System.Net.CredentialCache.DefaultCredentials Dim item As CatalogItem For Each item In rs.ListChildren("/SampleReports", False) If item.Type = ItemTypeEnum.Report Then DropDownList1.Items.Add(New ListItem(item.Name, item.Path)) End If Next End Sub

    如果您正在一個 Intranet 環境中部署應用程序,您要通過將 添加到應用程序的 web.config 文件中來啟用模擬。您還要將客戶端登錄憑據傳遞到該服務,如前面的示例所示。


    ListChildren 方法返回 CatalogItem 對象的數組,該數組表示用戶有權查看的項,包括報表、文件夾和數據源。Path 屬性提供了到項的唯一路徑,例如,/SampleReports/Sales Order Detail,Type 屬性使您可以區別報表和其他類型的項。


    既然我已經將報表名稱加載到了下拉列表中,我必須決定如何在儀表板應用程序內顯示報表。我決定擴展我早期編寫的 ReportViewer 代碼并且只要用戶選擇某個報表并點擊“Go”按鈕就更改 ReportPath 屬性。您還可以使用 IFRAME 并將 SRC 設置為適當的 URL(并為報表的名稱加上前綴“http://MyServer/ReportServer?”)。


    除了報表管理功能外,Web 服務提供的其他主要功能就是報表呈現。大多數應用程序(包括報表管理器應用程序)都使用 Web 服務功能來列出報表或更改設置,并使用我介紹過的 URL 方法來顯示報表。但是,如果您想要完全控制報表內容在應用程序中處理的方式,可以使用 ReportingService 類的 Render 方法。該方法返回一個可以在以后保存到輸出文件的字節數組;取決于請求的格式,輸出文件可以是圖形文件或 Excel 電子表格。如果您要以 HTML 格式呈現報表,可能會更加復雜一些,因為您將需要使用 RenderStream 方法來分別呈現圖像或其他資源。


    盡管本文的討論重點在于 Web 應用程序,您也可以使用相同的技術將報表生成功能添加到基于 Windows 的應用程序中。最簡單的方法仍是使用 URL 訪問,或者從應用程序啟動用戶的瀏覽器,或者包括 Microsoft WebBrowser ActiveX? 控件并使用 Navigate 方法來加載適當的 URL。當您想要更好地控制某些操作(例如,在圖片框中顯示結果或將文件直接保存到磁盤的操作)時,Web 服務的 Render 方法是一個很好的備選方法。


    信息工作者通常必須處理兩種類型的報表:當他們需要收集信息時他們想要獲得的類型以及每周都出現在他們桌面上或他們電子郵件中的類型。至此,我已重點闡述了用戶獲取他們自己的報表的過程,但 Reporting Services 還包括一種非常強大的稱為訂閱的功能,它使您可以將報表“推”給客戶。


    訂閱報表

    Reporting Services 訂閱功能讓用戶按照計劃接收報表。報表通常會通過電子郵件發送到用戶,但 Reporting Services 同時還讓您將報表生成到一個文件共享甚至可以編寫自己的提交擴展。這使您可以輕松地完成分發任務,例如,向每個銷售代表以電子郵件方式發送每周銷售統計報表,或者設置每月財政報表在每個月的最后一天運行,并將其作為一個 PDF 寫出到公司文件共享。


    在設置訂閱前,您需要考慮報表將如何連接到數據庫以檢索其數據。因此,訂閱的報表將不會由用戶直接執行,您無法為數據源使用集成的安全性,但在報表運行時必須指定要使用的用戶名和密碼。如果您試圖為使用集成安全性的報表設置訂閱,Reporting Services 將會返回一個錯誤。


    要更改示例報表的數據源,請打開“Report Manager | Sample Reports”,然后單擊 AdventureWorks 數據源。該數據源由所有示例報表共享,因此對它所做的任何更改將會影響所有報表。選中“Credentials stored securely in the report server”選項,指定一個具有訪問 AdventureWorks 數據庫權限的帳戶的有效域用戶名和密碼,選中“Use as Windows credentials when connecting to the data source option”,然后單擊“Apply”按鈕。請注意,這種更改意味著指定的帳戶將始終用于連接該數據庫,不管實際上是哪個用戶運行該報表。


    因此,您已經準備好深入研究并查看提供對訂閱訪問的 Web 服務 API。使用 Web 服務的這一部分比使用 List 或 Render 方法要多一些挑戰,主要原因在于可用選項的數量。我將要使用的方法是 CreateSubscription,它也屬于 ReportingService 類。有關該方法的參數的詳細說明,請參閱 ReportingService 類,但最基本的是您需要指定要訂閱的報表、生成計劃(例如,每周一早晨或每月的最后一個周五)、電子郵件選項(包括電子郵件地址)以及所有報表參數值。


    我已經將名為 EmailSubscriber 的實用工具類包括在本文的示例代碼中,該示例代碼摘要了某些復雜的內容。儀表板應用程序使用該類允許用戶指定他們的電子郵件地址并使 Employee Sales Summary 報表通過電子郵件每周一發送給他們。在產品應用程序中,您可以調整每個用戶的報表,例如,當您創建訂閱時,可以根據員工的 Windows 用戶帳戶檢索他們的員工 ID,然后適當地設置報表參數。


    如果您不希望等到下周一才測試您的訂閱,它有助于您了解有關 Reporting Services 如何處理訂閱計劃的更多信息。當您創建一個訂閱時,Reporting Services 會創建一個根據要求的計劃執行的 SQL Server 代理作業。您可以通過打開 Enterprise Manager,展開 Management、SQL Server Agent 和 Jobs 文件夾來查看這個作業。該訂閱作業將具有“Report Server”類別,并以由 Reporting Services 用來跟蹤該作業的 GUID 命名。右鍵單擊該作業并選擇 Start Job,如果您已正確地設置了您報表的方方面面,您的報表將會發送電子郵件給您。


    接下來該怎么做?

    您可以用來向應用程序中添加重要報表生成功能的兩個其他 Reporting Services 功能包括 Data Driven Subscriptions 和 Snapshots,前者使您可以設置用戶的郵件列表并以電子郵件形式將特定參數化的報表發送給他們,后者是在計劃點的報表視圖,同時還可以提供報表的歷史視圖。Reporting Services 是使用模塊化的體系結構構建的,如果您需要更強大的功能,它可以使您使用自己喜好的、以 .NET 為目標的語言來添加功能強大的新擴展。

    John C. Hancock 是 Microsoft 的高級顧問,致力于商務智能和 .NET 開發。他現在定居在多倫多,要聯系他,請訪問 http://www.johnchancock.net

    摘自 2004 年 8 月刊的 MSDN Magazine
    您可以在本地報刊亭購買或者最好 訂閱

    posted on 2005-12-08 21:18 天外飛仙 閱讀(1026) 評論(0)  編輯  收藏 所屬分類: .net
    主站蜘蛛池模板: 天堂亚洲免费视频| 久久国产乱子精品免费女| 最近2022中文字幕免费视频| 亚洲日韩aⅴ在线视频| 曰批免费视频播放免费| 国产传媒在线观看视频免费观看 | 亚洲成人一级电影| 最近新韩国日本免费观看 | 亚洲综合中文字幕无线码| 免费在线视频你懂的| 亚洲an日韩专区在线| 毛色毛片免费观看| 亚洲av日韩av永久无码电影| 免费a级毛片无码av| fc2成年免费共享视频网站| 亚洲欧洲日产国码无码网站| 精品亚洲永久免费精品| 精品亚洲麻豆1区2区3区| 亚洲免费观看在线视频| 亚洲欧美乱色情图片| 亚洲欧洲中文日韩久久AV乱码| 91在线免费观看| 自怕偷自怕亚洲精品| 嫩草影院免费观看| 免费无码一区二区| 亚洲AV午夜成人影院老师机影院| 亚洲免费在线视频播放| 亚洲国产欧洲综合997久久| 亚洲国产中文v高清在线观看| 免费看成人AA片无码视频吃奶| 蜜芽亚洲av无码精品色午夜| 毛片网站免费在线观看| 五月天婷婷精品免费视频| 国产亚洲综合网曝门系列| 亚在线观看免费视频入口| 亚洲人成电影网站久久| 免费h黄肉动漫在线观看| 无码av免费一区二区三区试看| 亚洲人成电影在线观看网| 亚洲精品第一国产综合境外资源| 久草视频在线免费看|