查看單個文章
舊 2006-10-19, 03:04 AM   #14 (permalink)
psac
榮譽會員
 
psac 的頭像
榮譽勳章
UID - 3662
在線等級: 級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時
註冊日期: 2002-12-07
住址: 木柵市立動物園
文章: 17381
現金: 5253 金幣
資產: 33853 金幣
預設

定義服務合同
每個 Indigo 服務類均需實現一些方法,以供其客戶端使用。服務類的創建者通過將這些方法包含在某個服務合同中,來決定將哪些方法公開為客戶可呼叫的操作。定義服務合同,實際上通常就是顯式使用服務,對 .NET 領域來說基本上是一個新觀念。Indigo 的創建者需要找到一條途徑,從 CLR 以及在其基礎上構建的編程語言的角度來把握這一觀念。幸運的是,CLR 的創建者們早就預見到了這種擴展需求,因而提供了對「內容」的支持。從開發人員的角度來看,內容就是一些字元串,可能具有關聯的內容,它們可能出現在類定義、方法定義的前面,也可能出現在其他位置。只要有內容出現,它就會改變其所關聯的事物的某些行為。
.NET Framework 從初始版本開始就對各種事物使用了內容。例如,在 .NET Framework 的 ASMX 技術中要使一個方法成為 SOAP 可呼叫的 Web 服務,該方法將被前置一個 WebMethod 內容。與此相似,企業服務使用 Transaction 內容來指示一個方法需要事務。Indigo 將這種觀念用於服務,定義了大量內容來定義和控制服務。
Indigo 中最基本的內容是 ServiceContract。實際上,Indigo 服務類本身就是標記有 ServiceContract 內容的類或者是實現了標記有該內容的接頭的類。以下是採用第一種方法的一個簡單 C# 示例:
using System.ServiceModel;
[ServiceContract]
class Calculator
{
[OperationContract]
private int Add(int a, int b)
{
return a + b;
}

[OperationContract]
public int Subtract(int a, int b)
{
return a - b;
}

public int Multiply(int a, int b)
{
return a * b;
}
}
ServiceContract 內容以及 Indigo 使用的所有其他內容均在 System.ServiceModel 命名空間中定義,因此本例開頭使用 using 語句來引用該命名空間。服務類中可被客戶端呼叫的每個方法都必須使用名為 OperationContract 的另一個內容加以標記。服務類中帶有前置 OperationContract 內容的所有方法都將自動被 Indigo 公開為 SOAP 可呼叫操作。在本例中,Add 和 Subtract 均標記有該內容,因此兩者均對該服務的客戶端公開。服務類中未標記有 OperationContract 的任何方法(如上例中的 Multiply)將不包含在服務合同中,因而不能被該 Indigo 服務的客戶端呼叫。
服務和對象,這兩個本來互不相干的抽像,在 Indigo 中卻走到了一起。必須理解的是,兩者均顯式或隱式地依賴於合同來定義它們將向外界公開什麼。通過類定義的對象有效地定義了一種合同,此合同決定了它的哪些方法可以被同一應用程式中的其他對像呼叫。對這些方法的訪問由語言關鍵字如 public 和 private 控制。例如,在上面所示的類 Calculator 中,同一應用程式中的其他對象可以呼叫該類的兩個公共方法 Subtract 和 Multiply。該類公開的對象合同中只包含這兩個方法。
通過 Indigo 的內容,Calculator 還定義了一個服務合同,如前所述。此合同也擁有兩個方法,但它們與對像合同中的那些方法不同。一個方法能否被此 Indigo 服務的客戶端呼叫,由 OperationContract 內容控制,而不是由 public 和 private 關鍵字控制。由於此內容只出現在 Add 和 Subtract 上,因此只有這兩個方法才能被客戶端呼叫。對像合同與服務合同彼此完全獨立,正因如此,同一方法(如 Add)才可以既是 private 同時又具有 OperationContract 內容。
剛才的示例展示了創建 Indigo 服務類的最簡單方法:直接使用 ServiceContract 標記類。這樣做之後,該類的服務合同將隱含定義為包含該類中所有標記有 OperationContract 的方法。還可以(並且大多數情況下這樣做會更好)使用語言中的 interface 類型顯式地指定服務合同。使用這種方法,Calculator 類可能會如下所示:
using System.ServiceModel;
[ServiceContract]
public interface ICalculator
{
[OperationContract]
private int Add(int a, int b);
[OperationContract]
public int Subtract(int a, int b);
}
class Calculator :ICalculator
{
private int Add(int a, int b)
{
return a + b;
}

public int Subtract(int a, int b)
{
return a - b;
}

public int Multiply(int a, int b)
{
return a * b;
}
}
在本例中,ServiceContract 和 OperationContract 內容被指定到 ICalculator 接頭及其包含的方法,而不是 Calculator 類本身。但結果是相同的,因此這一版本的服務公開的服務合同與前一版本相同。像這樣使用顯式接頭稍微有點複雜,但同時也具有更大的靈活性。例如,一個類可以實現多個接頭,這意味著它也可以實現多個服務合同。通過公開多個終結點,每個終結點擁有一個不同的服務合同,一個類就可以向不同的客戶端提供不同的服務組。
最後一點:使用 ServiceContract 和 OperationContract 標記服務類還允許以 Web 服務描述語言 (WSDL) 自動產生服務合同定義。因此,可將每個 Indigo 服務合同的外部可見定義作為指定該合同中的操作的標準 WSDL 文檔來訪問。雖然本文不作講解,但直接從 WSDL 文檔創建 Indigo 服務類的做法同樣是可行的,這一方法對於實現外部定義的 WSDL 接頭十分有用。
定義資料合同
Indigo 服務類指定了一個服務合同,該服務合同定義了將向服務客戶端公開哪些方法。這些操作中的每個操作一般都將傳遞一些資料,即一個服務合同還隱含有某種資料合同,該資料合同用以描述將被交換的訊息。有些情況下,這種資料合同被作為服務合同的一部分來隱含定義。例如,在上面所示的 Calculator 類中,每個方法使用了兩個整數型輸入參數並返回一個整數。這些參數定義了此服務交換的所有資料,因此它們包含服務的資料合同。對於這種每個操作僅使用簡單類型的服務,在服務合同內隱含定義該合同的資料特性較為恰當。無需再進行任何其他操作。
但服務還可能擁有更複雜類型的參數,如結構。在這種情況下,就需要使用顯式資料合同。資料合同定義記憶體中的類型如何轉換為適合通過線路傳輸的形式,即所謂的「序列化」過程。實際上,資料合同是控制資料如何序列化的一種機制。
在 Indigo 服務類中,資料合同使用 DataContract 內容來定義。標記有 DataContract 的類、結構或其他類型都可以擁有一個或多個帶有前置 DataMember 內容的成員,指示該成員必須被包含在此類型的序列化值中。以下是一個簡單示例:
[DataContract]
struct Customer {
[DataMember] public string Name;
int public age;
[DataMember] private int CreditRating;
}
當將此 Customer 類型的實例作為一個參數在標記有 OperationContract 的方法中傳遞時,將只有那些標記有 DataMember 內容的字段(Name 和 CreditRating)被傳遞。
字段標記為 public 或 private 對該字段是否被序列化沒有影響。就像方法的情況一樣,public 和 private 關鍵字是定義此類型如何被同一應用程式中的其他對像訪問的合同的一部分。DataMember 則像 OperationContract 一樣,定義該類型如何被此類所實現的服務的客戶端訪問。兩者再次完全獨立。
關於 Indigo 合同需要強調的最後一點是,沒有任何東西預定成為服務合同或資料合同的一部分。相反,開發人員必須顯式地使用 ServiceContract 和 DataContract 內容指示哪些類型擁有 Indigo 定義的合同,然後使用 OperationContract 和 DataMember 內容顯式地指定這些類型的哪些部分向此服務的客戶端公開。其設計者的基本原則之一便是服務必須擁有顯式邊界,因此 Indigo 是一種明確選擇技術。服務要向其客戶端提供的任何事物均需在代碼中明確指定。
合同以及用於定義它們的那些內容是 Indigo 的主要特性,此處的簡短描述僅涵蓋了最顯著的部分。OperationContract 內容可用於定義「單向」操作,例如不需要應答的服務呼叫。還可通過創建「雙工」合同定義雙方均可充當客戶端和服務的交互操作,每一方均可呼叫操作並公開對方呼叫的操作。DataContract 內容同樣擁有多個選項,甚至可以使用一個稱為 MessageContract 的內容直接與 SOAP 消息進行內部交互。Indigo 提供的大部分操作均通過合同來表示,因此合同是其最基本的概念。
選擇宿主
實現 Indigo 服務的類通常編譯到庫中。按照定義,所有庫都需要執行在宿主應用程式域和 Windows 工作行程中。Indigo 提供了兩種實現服務的宿主庫的方法。一種是使用宿主應用程式域和由 Windows 啟動服務 (WAS) 提供的工作行程,而另一種是允許服務托管於執行在任意工作行程內的任何應用程式域中。本節將從 WAS 開始對兩者進行介紹。
使用 Windows 啟動服務托管服務
托管 Indigo 服務的最簡單方式就是依靠 WAS。(請注意,在 Indigo 的第一個社區技術預覽中不支持 WAS。相反,Indigo 服務可托管在 Windows Server 2003 和 Windows XP 上的 Internet 訊息服務器中,雖然在該配置中只支持通過 HTTP 傳遞的 SOAP。)使用 WAS 與使用 IIS 提供的用於 ASMS 的托管機制很相似。其中,兩者均依靠「虛擬目錄」的概念,它是 Windows 文件系統中的實際目錄路徑的一個簡短別名。
要檢視 WAS 托管如何工作,假設前面所述的兩個 Calculator 類中有一個類被編譯到名為 calc.dll 的庫中,然後放置到執行於 Windows Server 2003 上的系統的虛擬目錄 calculator 中。要指示 calc.dll 中實現的 Indigo 服務需要使用 WAS 托管,開發人員應在 Calculator 虛擬目錄中創建一個帶有 .svc(當然是表示「服務」的意思)延伸名的文件。對於我們的簡單示例,此文件可能稱為 calc.svc,其整個內容可以是:
%@Service language=c# class="Calculator" %
一旦完成這一步,且如下一節所示定義了一個終結點,則客戶端對 Calculator 服務的某個方法的請求將自動創建一個該類的實例,以執行指定的操作。該實例將執行在由 WAS 提供的標準工作行程中創建的應用程式域中。
在任意工作行程中托管服務
依靠 WAS 提供一個工作行程來托管 Indigo 服務無疑是最簡單的選擇。但應用程式經常需要從其自己的工作行程中公開服務,而不是依靠 Windows 提供的工作行程。幸運的是,這樣做並不困難。下面的示例顯示了如何創建托管前面定義的兩種 Calculator 類的工作行程。


using System.ServiceModel;
public class CalculatorHost
{
public static void Main()
{
ServiceHost s1 =
new ServiceHost();
s1.Open();
Console.Writeline("Press ENTER to end service");
Console.Readline();
}
}
由於類 CalculatorHost 包含一個 Main 方法,因此它將作為一個獨立的工作行程執行。要托管示例 Calculator 服務,此方法必須創建一個新的 ServiceHost 類的實例,在 Calculator 類中進行傳遞。(請注意,這種標準 Indigo 類屬於「泛型」,通過括住其參數的 指示。泛型是版本 2.0 的 C#、Visual Basic .NET 及其他基於 .NET Framework 2.0 的語言中的一種新語言特性。)一旦創建此類的實例,則使服務可用所需的唯一操作就是對該實例呼叫 Open 方法。這時 Indigo 將自動將來自客戶端的請求轉到 Calculator 類中相應的方法。
要使 Indigo 服務能夠處理來自其客戶端的請求,托管它的工作行程必須始終在執行。對於 WAS 托管的服務這不是問題,因為 WAS 提供的標準工作行程可以確保這一點。然而宿主應用程式則必須自己解決這個問題。在這個簡單的示例中,工作行程將按等待控制台用戶輸入的簡單機制持續執行。
定義終結點
除了在 Indigo 服務類中定義操作並指定執行這些操作的宿主工作行程,Indigo 服務還必須公開一個或多個終結點。每個終結點將指定以下三項內容:

一個「合同」名稱,指示該 Indigo 服務類通過該終結點公開哪個服務合同。一個標記有 ServiceContract 且未實現任何顯式接頭的類(例如前面第一個示例中所示的 Calculator)只能公開一個服務合同。在這種情況下,所有終結點將公開同一合同。但是,如果一個類顯式實現了兩個或多個標記有 ServiceContract 的接頭,則不同的終結點就可以公開不同的合同。

一個「位址」,指示該終結點位於何處。位址為標識一台電腦以及該電腦上的一個特定終結點的 URL。

一個「綁定」,決定如何訪問該終結點。綁定決定可以使用哪些協議組合來訪問該終結點,還決定了其他一些內容,如通信是否可靠以及可採用哪些安全機制。例如,假設一個服務的創建者希望允許客戶端使用通過 HTTP 或 TCP 傳遞的 SOAP 來訪問該服務。它們每個都是一個不同的綁定,因此該服務需要公開兩個終結點,一個擁有 SOAP-over-HTTP 綁定,另一個擁有 SOAP-over-TCP 綁定。
綁定是實現通信的關鍵部分。為了使它們更易於使用,Indigo 包含了一組預先定義好的綁定集合,其中每個綁定指定一個特定的選項組。這個集合包括:

BasicProfileHttpBinding:遵循 Web Services Interoperability Organization (WS-I) Basic Profile 1.0,該規範定義了通過 HTTP 傳遞的 SOAP。這是未顯式指定時,終結點的預定綁定方式。

BasicProfileHttpsBinding:遵循 WS-I Basic Security Profile 1.0,該規範定義了通過 HTTPS 傳遞的 SOAP。

WsHttpBinding:支持採用 WS-ReliableMessaging 的可選消息傳輸、採用 WS-Security 的安全性以及採用 WS-AtomicTransaction 的事務。此綁定允許與同樣支持這些規範的其他 Web 服務實現之間進行互操作。

WsDualHttpBinding:與 WsHttpBinding 類似,但還支持採用雙工合同的交互。使用此綁定,服務和客戶端均可接收和發送消息。

NetTcpBinding:直接通過 TCP 發送二進制編碼的 SOAP,包括對可靠消息傳輸、安全性和事務的支持。此綁定只能用於 Indigo 對 Indigo 通信。

NetNamedPipeBinding:通過命名管道發送二進制編碼的 SOAP。此綁定只能用於同一 Windows 電腦上兩個工作行程之間的 Indigo 對 Indigo 通信。

NetMsmqBinding:通過後面將要介紹的 MSMQ 發送二進制編碼的 SOAP。此綁定只能用於 Indigo 對 Indigo 通信。
http://www.microsoft.com/china/MSDN/library/windev/longhorn/art/introindigov1-005.gif
上圖顯示了前面所示第一個 Calculator 服務的一個終結點的三個元素中每個元素的示例值。服務合同的名稱為 Calculator,也就是實現該服務的類的名稱,綁定則是 BasicProfileHttpBinding。假設此服務採用 WAS 托管,安裝在前面所述的虛擬目錄 calculator 中,並執行在一台名稱為 qwickbank.com 的電腦上,則其位址可能為 http://www.qwickbank.com/calculator/calc.svc。
與合同不同,終結點不使用內容來定義。雖然可以通過編程方式創建終結點,但最通用的方式大概還是使用與該服務關聯的配置文件來實現。WAS 托管的服務使用 web.config 文件,而那些獨立托管的服務則使用與它們執行於其中的應用程式關聯的配置文件(通常為 app.config,實際文件名可能會有變化)。如果僅用於前面所示的第一個 Calculator 服務類,此配置文件可能如下所示:
一個 Indigo 應用程式實現的所有服務的配置訊息均包含在 system.serviceModel 元素內。此元素包含一個 services 元素,而後者又包含一個或多個 service 元素。這個簡單的示例僅有一個服務,因此只出現了一個 service。service 元素的 serviceType 內容標識了實現該配置所應用的服務的服務類,在本類中就是 Calculator。每個 service 元素可以包含一個或多個 endpoint 元素,其中每個元素指定一個可通過它訪問此 Indigo 服務的特定終結點。在本例中,服務僅公開了一個終結點,因此僅出現了一個 endpoint 元素。終結點合同的名稱為 Calculator,也就是實現它的類的名稱。如果此配置文件用於前面所示的第二個 Calculator 服務,即使用顯式接頭定義其服務合同的服務,則 serviceType 內容的值將不變,但 contractType 的值將替換為 ICalculator,即該顯式接頭的名稱。此處指定的綁定為 basicProfileHttpBinding,但由於它是預定設置,因此可以省略。假設 Calculator 是一個 WAS 托管的服務,則位址將自動產生,因此不需要在此配置文件中指定。

http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif
[url=http://www.microsoft.com/china/MSDN/library/windev/longhorn/introindigov1.mspx#top]
創建 Indigo 客戶端
創建基本 Indigo 服務沒有什麼特別複雜的地方。創建 Indigo 客戶端則更加簡單。需要做的一切就是為服務創建一個稱為「代理」的本機替身,它連接到目標服務的某個特定終結點,然後就是通過代理呼叫該服務的操作。下圖顯示了此原理。
http://www.microsoft.com/china/MSDN/library/windev/longhorn/art/introindigov1-006.gif
創建代理需要準確知道目標終結點公開的合同,然後使用該合同的定義產生代理。在 Indigo 中,此過程是由一個稱為 svcutil 的工具完成的。如果服務採用 Indigo 實現,svcutil 可以訪問該服務的 DLL 以瞭解合同並產生代理。如果只有服務的 WSDL 定義可用,則 svcutil 可讀取它以創建代理。如果只有服務本身可用,svcutil 可通過 WS-MetadataExchange 或一條簡單的 HTTP GET 命令直接訪問它,以獲得該服務的 WSDL 接頭定義,然後產生代理。
不管它是如何產生的,客戶端均可創建該代理的一個新實例,然後通過該實例呼叫服務的方法。以下是 Calculator 類的一個客戶端的簡單示例:
using System.ServiceModel;
using Indigo.Example; // 所產生的代理類的命名空間
public class CalculatorClient
{
public static void Main()
{
CalculatorProxy p = new CalculatorProxy();
Console.WriteLine("7 + 2 = {0}", p.Add(7, 2));
Console.WriteLine("7 - 2 = {0}", p.Subtract(7, 2));
p.Close();
}
}
還有一項內容客戶端需要定義:它要呼叫操作的具體終結點。與服務一樣,客戶端必須指定終結點的合同、其綁定以及其位址,這些通常是在配置文件中完成的。實際上,如果有足夠的訊息可用,svcutil 將自動為目標服務產生適當的客戶端配置文件。

http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif
[url=http://www.microsoft.com/china/MSDN/library/windev/longhorn/introindigov1.mspx#top]]
__________________
http://bbsimg.qianlong.com/upload/01/08/29/68/1082968_1136014649812.gif
psac 目前離線  
送花文章: 3, 收花文章: 1631 篇, 收花: 3205 次