今天繼續我們<WCF分布式開發必備知識>系列文章第3節,通過前兩節的學習,我們已經掌握了MSMQ和.Net Remoting的概念和開發過程.今天我們來學習.Net分布式開發的另外一個技術:Enterpise Services.
本節的基本結構是
1.EnterpiseServices和COM+的基本概念.
2.Enterpise Services中重要的類及特性
3.Enterpise Services實現COM+事務的編碼\部署\測試過程.最後是總結.
那麼現在我們就開始今天的學習,首先介紹的是:
一.基本概念
1.什麼是COM+
COM+的底層結構仍然以COM為基礎,它不僅具備了COM的易於用戶定制\可重用\簡化開發的優點,同時又避免了COM實現方面的一些不足.把COM、DCOM和MTS的編程模型結合起來,它通過操作系統的各種支持,使組件對象模型建立在應用層上,繼承了它們的絕大多數特性,增加了新的功能。 COM+的幾個主要特性: 比如隊列服務、負載平衡、內存數據庫、事件服務等。它更加注重於分布式網絡應用的設計和實現(參考msdn)。
2.什麼是Enterpise Services
Enterpise Services是微軟應用程序服務器技術的別稱..Net Enterpise Services提供了可以在.Net 組件中使用的COM+服務.因為它也是基於以前的COM+技術,在.NET平台上開發.NET組件,使用Enterpise Services可以將.NET組件並進行封裝為COM對象,這樣.NET組件就可以使用COM+服務了..NET做為新一代的開發平台,實現了COM組件相互之間的調用,繼承了向前兼容的優良傳統.同樣COM+可以調用.Net 組件,會給COM+服務帶來一些擴展.
二.Enterpise Services中重要的類及特性
Enterpise Servicesg工作在三層架構中的業務邏輯層或者數據訪問層.由於基於COM+技術,所以它也可以通過Micro Application Center Server實現負載均衡.下面我們來介紹一下Enterpise Services裡幾個重要的概念.
(1)上下文(context又翻譯為環境):所有的COM+服務都是通過上下文(context又翻譯為環境)實現的,上下文是進程裡提供給對象運行時服務的空間.如果一個對象調用另外一個上下文裡的對象,這個方法調用會被代理截取,COM運行時就可以利用代理來預處理或者遲處理請求並執行相關的服務代碼.過程如圖1
圖1COM+服務與上下文
(2)自動事務處理(automatic transaction):
Enterpise Services裡最常用的特性就是自動事務處理,這個一般在類的星星[Transaction]使用這個特性,就不需要再編寫復雜的代碼來顯示執行事務准備工作,如對象的傳遞等,設置這個屬性後,上下文環境會在後台進行設置.具體的設置在TransactionOption類的屬性裡選擇.Required表示共享一個事務,必要的時候創建一個新事務.
(3)對象池(Obejct pooling):
這個屬性可以設置是需要對象池和對象池的大小.主要是考慮到對象的創建和銷毀會占用更多的資源.使用對象池會事先創建對象,客戶的請求到來就直接到對象池裡查找需要的對象,直接響應請求,可以提高服務的性能,節約時間.
(4)ServicedComponent:
是所有使用COM+服務類的基類.ServicedComponent繼承自ContextBoundObject類.ContextBoundObject繼承自MarshalByRefObject.這個類在上一節的.Net Remoting介紹過,使支持remoting的程序可以跨程序域邊界的訪問對象.
另外還有分布式事務處理的等概念,Enterpise Services裡是使用DTC來實現的.
三.Enterpise Services實現COM+事務的編碼\部署\測試過程
上面我們熟悉了Enterpise Services的特性和一些重要的概念,下面我們就來利用Enterpise Services技術實現裡COM+事務處理.在分布式應用程序中,我們往往需要同時操作多個數據庫,使用數據庫本身的事務處理,很難滿足程序對事務控制的要求。
大家都知道事務具有ACID的特性:Aotomicity原子性\Consistency一致性\Isolation獨立性\Durability永久性.
原子性就是一組數據庫操作被視為一個單元。要麼所有的操作都成功;如果其中一個操作失敗,則整個事務失敗。事物失敗,不會提交更改。
具體的實現過程如下:
1.編寫Enterpise Services COM+事務服務組件:
首先我們使用Visual Studio 創建一個類庫項目,添加程序集System.EnterpriseServices的引用.添加類EnterpriseServicesCOMPlus繼承自ServicedComponent,確保可以使用COM+服務.命名空間外設置程序集屬性.具體代碼
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Data;
5 using System.Data.SqlClient;
6 using System.EnterpriseServices;
7[assembly: ApplicationName("FrankEnterpriseServicesTransactionTest")]//在組件服務浏覽器裡的名稱
8[assembly: Description("Coded By Frank Xu Lei 2/14/2009")]//在組件服務浏覽器裡的描述信息
9[assembly: ApplicationActivation(ActivationOption.Server)]//啟動類型Library在創建進程裡激活,運行在客戶進程,Server類型系統進程激活,
10[assembly: ApplicationAccessControl(false)]//訪問設置,關閉後,用戶都可以訪問
11namespace EnterpriseServicesCOM
12…{
13 //每個服務器組件必須繼承自類ServicedComponent,ServicedComponent類繼承自ContextBoundObject
14 //這樣服務就可以綁定到.net環境上
15 //
16 [EventTrackingEnabled(true)]//允許監視,組件對象浏覽器可以看到。默認關閉,因為會降低性能
17 [Description("Coded By Frank Xu Lei For EnterpriseServices Transaction Test")]//組件對象浏覽器顯示的描述信息
18 //[JustInTimeActivation(false)]//打開或者關閉JIT啟動
19 [ObjectPooling(true, 1, 10)]//是否啟用對象池,如果對象創建消耗更多的資源,可以考慮對象池
20 [Transaction(TransactionOption.Supported)]//是否需要支持事務特性
21
22 public class EnterpriseServicesCOMPlusTransaction : ServicedComponent
23 …{
24 //構造函數
25 public EnterpriseServicesCOMPlusTransaction()
26 …{
27
28 }
29 //新增用戶user信息,姓名長度表name和address的長度都是10字符,user2的長度都是100字符,此處調用Ado.net據持久層方法
30 //[AutoComplete]//設置方法屬性,自動更新方法調用的結果
31 public bool AddUser(string name, string address)
32 …{
33 //調用另外一個COM組件的方法,主要是測試事務的完整性
34 SqlConnection Conn;
35 try
36 …{
37 //user,第1次執行的插入操作,字段長度限制10個字符
38 //使用DataSet向導創建方法操作數據庫,
39 EnterpriseServicesCOM.DatabaseDataSetTableAdapters.userTableAdapter _userTableAdapter = new EnterpriseServicesCOM.DatabaseDataSetTableAdapters.userTableAdapter();
40 _userTableAdapter.Insert(name, address);
41 /**/////user2,第2次執行的插入操作,字段長度限制100個字符
42 EnterpriseServicesCOM.DatabaseDataSetTableAdapters.user2TableAdapter _user2TableAdapter = new EnterpriseServicesCOM.DatabaseDataSetTableAdapters.user2TableAdapter();
43 _user2TableAdapter.Insert(name, address);
44 //也可以自己寫代碼實現數據庫的操作
45
46 //SqlConnection Conn = new SqlConnection("Data Source=|DataDirectory|Database.sdf");
47 //Conn.Open();
48 /**///// //user,第1次執行的插入操作,字段長度限制10個字符
49 //SqlCommand sqlCommand = new SqlCommand("INSERT INTO [user] (name, address) VALUES (" + name + "," + address + ")");
50 //sqlCommand.ExecuteNonQuery();
51 /**/////user2,第2次執行的插入操作,字段長度限制100個字符
52 //sqlCommand.CommandText = "INSERT INTO [user2] (name, address) VALUES (" + name + "," + address + ")";
53 //sqlCommand.ExecuteNonQuery();
54
55 ContextUtil.SetComplete();//事務成功
56 //Conn.Close();
57 return true;
58 }
59 catch (Exception e)
60 …{
61 ContextUtil.SetAbort();//失敗,事務回滾,終止
62 return false;
63 throw e;
64
65 }
66 finally
67 …{
68 //if (Conn != null)
69 //{
70 // Conn.Dispose();//釋放資源
71 // Conn = null;
72 //}
73 }
74 }
75 //更新數據庫的2個表,此處可以調用數據持久層方法
76 //[AutoComplete]//設置方法屬性,自動更新方法調用的結果
77 public bool UpdateUser(string name, string address, int id)
78 …{
79 //調用另外一個COM組件的方法,主要是測試事務的完整性
80 SqlConnection Conn;
81 try
82 …{
83 //使用DataSet向導創建方法操作數據庫,
84 EnterpriseServicesCOM.DatabaseDataSetTableAdapters.userTableAdapter _userTableAdapter = new EnterpriseServicesCOM.DatabaseDataSetTableAdapters.userTableAdapter();
85 _userTableAdapter.Update(name,address,id);
86
87 EnterpriseServicesCOM.DatabaseDataSetTableAdapters.user2TableAdapter _user2TableAdapter = new EnterpriseServicesCOM.DatabaseDataSetTableAdapters.user2TableAdapter();
88 _user2TableAdapter.Update(name, address, id);
89 //也可以自己寫代碼實現數據庫的操作
90 //SqlConnection Conn = new SqlConnection("Data Source=|DataDirectory|Database.sdf");
91 //Conn.Open();
92 /**/////
93 //SqlCommand sqlCommand = new SqlCommand("UPDATE [user] SET name = " + name + ", address =" + address + "WHERE (id =" + id + ")");
94 //sqlCommand.ExecuteNonQuery();
95
96 //sqlCommand.CommandText = "UPDATE [user2] SET name = " + name + ", address =" + address + "WHERE (id =" + id + ")";
97 //sqlCommand.ExecuteNonQuery();
98
99 ContextUtil.SetComplete();//事務成功
100 return true;
101 //Conn.Close();
102 }
103 catch (Exception e)
104 …{
105 ContextUtil.SetAbort();//失敗,事務回滾,終止
106 return false;
107 throw e;
108 }
109 finally
110 …{
111 //if (Conn != null)
112 //{
113 // Conn.Dispose();//釋放資源
114 // Conn = null;
115 //}
116 }
117 }
118 }
119}
120
[assembly: ApplicationName("FrankEnterpriseServicesTest")],在組件服務浏覽器裡的名稱
[assembly: Description("Coded By Frank Xu Lei 2/11/2009")],在組件服務浏覽器裡的描述信息
[assembly: ApplicationActivation(ActivationOption.Server)],啟動類型Library在創建進程裡激活,運行在客戶進程,Server類型系統進程激活.
[assembly: ApplicationAccessControl(false)],訪問設置,關閉後,用戶都可以訪問.
每個服務器組件必須繼承自類ServicedComponent,ServicedComponent類繼承自ContextBoundObject,這樣服務就可以綁定到.net remoting上下文環境上.
[EventTrackingEnabled(true)],允許監視,組件對象浏覽器可以看到。默認關閉,因為會降低性能.
[Description("Coded By Frank Xu Lei For EnterpriseServices Test")],組件對象浏覽器顯示的描述信息.
//[JustInTimeActivation(false)],打開或者關閉JIT啟動.
[ObjectPooling(true,1,10)],是否啟用對象池,如果對象創建消耗更多的資源,可以考慮對象池.
[Transaction(TransactionOption.Required)].是否需要支持事務特性,必要時候創建新的事務.
設置一個 try 塊以捕獲在數據庫處理過程中可能出現的任何異常。您必須捕獲這些異常來終止事務。
組件必須有一個強名稱。為了防止Dll hell.生成一個強名稱,然後使用該強名稱對程序集進行簽名。步驟如下:
(1)在 Visual Studio .NET 命令提示符處,鍵入 sn.exe -k FrankXuKey.snk 以創建一個密鑰文件.
(2)將 FrankXuKey.snk 復制到您的項目文件夾中。可以在項目屬性裡設置簽名文件.
(3)也可以在在 AssemblyInfo.vc 中,將以下代碼行添加到其他程序集屬性語句之前或之後:[assembly: AssemblyKeyFileAttribute("..\\..\\FrankXuKey.snk")]
(4)保存,重新編譯,然後bin目錄下生成dll文件。下面我們開始部署
2.部署服務組件:
使用regsvcs.exe將Dll注冊到COM+ Services裡面 ,在Visual studio命令行窗口輸入運行regsvcs.可以把生成的DLL文件拷貝到c盤更目錄下安裝也可以直接輸入路徑進行安裝,格式是:
regsvcs 文件名 .注冊的界面如下圖2:輸入命令,按回車鍵
圖2安裝結果
注冊成功,regsvcs.exe把dll輸入到COM+ Services中。
如果是服務器操作系統,注冊完成後可以在組件服務裡查看到安裝後的組件,如下圖3:
圖3對象浏覽器查看組件
可以看我們的組件服務和實現的接口信息.右鍵可以查看組件的信息如圖4.
圖4組件屬性
現在可以看到詳細組件屬性信息,包括安全\對象池等信息.我們還可以選擇導出組件為MSI文件,方便安裝.如圖5
導出的安裝文件,可以進行組件的快速部署安裝.導出文件下載/Files/frank_xl/FrankXuEnterpriseServicesMSI.rar.
3.簡單客戶端
創建簡單的控制台程序,方便調試,首先是循環調用組件服務EnterpriseServicesCOMPlus的SayHello方法.測試組件服務調用是否成功,其次是測試組件事物特性的代碼,具體信息如下:
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using EnterpriseServicesCOM;
5namespace EnterpriseServicesClient
6…{
7 //客戶端為控制台程序,方便測試
8 class EnterpriseServicesClient
9 …{
10 static void Main(string[] args)
11 …{
12 //創建COM對象,測試組件服務的一般調用
13 using (EnterpriseServicesCOM.EnterpriseServicesCOMPlus enterpriseServicesCOM = new EnterpriseServicesCOMPlus())
14 …{
15 for (int i = 0; i < 10; i++)
16 …{
17
18 string sName = "Frank Xu Lei";
19 string sMessage = enterpriseServicesCOM.SayHello(sName);//調用
20 Console.WriteLine(sMessage);
21 }
22 }
23 //創建COM對象,測試組件服務的事物特性
24 using (EnterpriseServicesCOM.EnterpriseServicesCOMPlusTransaction enterpriseServicesCOMTransaction = new EnterpriseServicesCOMPlusTransaction())
25 …{
26 //第一次事務操作成功,字段長度合法
27 if (enterpriseServicesCOMTransaction.AddUser("FrankXu", "ShangHai"))
28 …{
29 Console.WriteLine("Transaction1 is successful");
30 }
31 else
32 …{
33 Console.WriteLine(Console.WriteLine("Transaction1 is failed"));
34 }
35 //第2次事務操作失敗
36 if (enterpriseServicesCOMTransaction.AddUser("FrankXuLei2009", "ShangHai2009"))
37 …{
38 Console.WriteLine("Transaction1 is successful");
39 }
40 else
41 …{
42 Console.WriteLine(Console.WriteLine("Transaction1 is failed"));
43 }
44
45 }
46 Console.ReadLine();
47 }
48 }
49}
50
測試事物特性的代碼分了兩種情況,第一次調用新增用戶的方法事務成功,字符長度合法.第2次事務則提交失敗,字符插入數據庫異常.事務回滾.
四.總結
運行結果如下圖:
Transaction1執行成功,Transaction2執行失敗.ContextUtil.SetAbort();設置上下文環境的狀態信息,事物終止,該語句使所有的數據庫操作回滾,數據庫未增加新記錄.
今天我們詳細學習了.NET Enterprise Services基本知識,以及它和COM+的關系,熟悉了.NET Enterprise Services的事務特性,詳細的了解COM+組件服務的技術細節.希望能給大家一些啟發和幫助.歡迎交流.下一節打算寫分布式編程的另外一個技術Web Service.會附加一部分WSE的部分,另外延續本文的習慣,我會上穿完整的代碼/Files/frank_xl/EnterpriseServices.rar.本文參考了大牛Tim Ewald的文章COM+ Integration: How .NET Enterprise Services Can Help You Build Distributed Applications.很不錯的文章,好象還沒人翻譯,打算周末抽時間翻譯一下一起放出來,與大家分享.有興趣的朋友可以關注.