這篇文章描述了使用三種方法來實現或擴展一個通用服務協議(generic service contract),其中 包括實現一個通用服務協議、擴展一個服務協議以及多頭服務(Multi-Headed Service),這個實例中使 用的通用的服務協議是在Service Tutorial 8 (C#) - Generic Service Declaration.所創建的。
這個實例由C#實現,可以在下面的目錄中找到這個項目:
Samples"ServiceTutorials"Tutorial9"CSharp
第一步:創建項目
由於項目需要擴展一個通用服務協議(generic service contract),所以項目中需要引用Service Tutorial 8項目生成的dll文件,在Service Tutorial 9項目你會看到它實現了三個個服務,在一個項目 中實現多個服務的話,要確保這些服務的命名空間是不同的哦。
第二步:實現一個通用服務協議
第一個服務實例是GenericServiceImplementation,實現這個服務的文件是 GenericServiceImplementation.cs 和 GenericServiceImplementationTypes.cs,這個服務僅僅是實現 了一個通用服務協議。
服務類型聲明(Service Type Declarations):
因為在這個服務中使用了通用服務協議,因此它不需要我們去定義他的狀態和操作,這些在通用服務 協議已經定義好了,看看ServiceTutorial8就知道了,但是它還是需要一個協議標識(Contract identifier),因為需要用這個標識來找到這個服務,服務標識定義如下:
1 /// <summary> 2 /// Generic Service Implementation Contract Identifier 3 /// </summary> 4 public sealed class Contract 5 6 { 7 /// The Unique Contract Identifier for this service 8 9 [DataMember()] 10 public const String Identifier = "http://schemas.tempuri.org/2007/08/servicetutorial9/genericservice/implementation.html"; 11 } 12
引用通用協議(Referencing the Generic Contract)
在項目中引用通用服務協議代理程序集(generic contract's proxy assembly),一定要引用代理程 序集,即*Proxy.dll,這個文件可以在msrs安裝根目錄下的bin文件夾中找到,為這個通用服務協議的命 名空間定義一個別名,如下:
using generic = ServiceTutorial8.Proxy;
服務實現
這個服務的實現和其他的服務基本沒有什麼區別,有兩點如下:
1、 服務實現類需要用AlternateContract屬性標記,用來指示在這個服務中使用的是通用服務協議 。
1 /// <summary> 2 /// This service provides an implementation of the generic service 3 /// </summary> 4 [Contract(Contract.Identifier)] 5 [AlternateContract(generic.Contract.Identifier)] 6 [DisplayName("Service Tutorial 9: Generic Service Implementation")] 7 [Description("This service implements the generic service provided in Service Tutorial 8.")] 8 [DssServiceDescription("http://msdn.microsoft.com/library/bb727257.aspx")] 9 public class ImplementationService : DsspServiceBase 10
2、因為這個服務自己沒有定義狀態和操作,狀態和操作都在通用服務協議有所定義了,只需在服務中 使用罷了。如下:
1 // The state of this service is exactly that of the generic service 2 [ServiceState] 3 private generic.GenericState _state = new generic.GenericState(); 4 5 // The operations port is exactly that of the generic service 6 [ServicePort("/GenericServiceImplementation", AllowMultipleInstances = false)] 7 private generic.GenericServiceOperations _mainPort = new generic.GenericServiceOperations(); 8 9 /// <summary> 10 /// Default Service Constructor 11 /// </summary> 12 public ImplementationService(DsspServiceCreationPort creationPort) 13 : 14 base(creationPort) 15 { 16 } 17
剩下的工作是為通用協議中的定義的消息操作(message operations)添加處理方法(handler),如 下代碼定義了Get操作的處理方法:
1 /// Get Handler 2 /// </summary> 3 /// <param name="get"></param> 4 /// <returns></returns> 5 // Note that this service does not need to implement Get unless there are 6 // some specific functions that it must perform 7 [ServiceHandler(ServiceHandlerBehavior.Concurrent)] 8 9 public virtual IEnumerator<ITask> GetHandler(generic.Get get) 10 { 11 get.ResponsePort.Post(_state); 12 yield break; 13 } 14
運行服務
運行服務後,在浏覽器中Service Directory可以查看到兩個服務genericserviceimplementation 和 genericserviceimplementation/servicetutorial8.因為服務沒有擴展通用服務協議,所以這兩個服務的 狀態和操作是一樣的。
圖1,運行服務,如圖紅框標出DSS節點中運行了兩個服務(genericserviceimplementation and genericserviceimplementation/servicetutorial8),兩個服務有相同的狀態和操作類型。
第三步:擴展一個通用服務協議
第二個服務提供了對通用服務協議的實現,同時又添加了一個自己的狀態和消息操作,這樣做的目的 是:如果服務使用者只知道通用服務協議所定義狀態和操作的話,他可以很容易使用這個服務,並且,這 個服務還允許使用者使用擴展的狀態和操作。
服務類型聲明
和前面實現的服務不同,前面的服務沒有自己的服務類型聲明,而這個服務有自己服務類型聲明,包 括協議標識,服務狀態以及服務操作,其中服務狀態繼承自通用服務中的狀態。
協議標識聲明如下:
1 /// <summary> 2 /// Generic Service Extension Contract Identifier 3 /// /// </summary> 4 public static class Contract 5 { 6 public const string Identifier = "http://schemas.tempuri.org/2007/08/servicetutorial9/genericservice/extension.html"; 7 } 8 其中的服務狀態繼承自通用服務協議中的狀態,同時有所擴展,添加了Age屬性,繼承實現如下: 1 [DataContract] 2 [DisplayName("Service Tutorial 9: Extension Service State")] 3 [Description("This service state extends the generic service state provided in Service Tutorial 8.")] 4 public class ExtensionState : generic.GenericState 5 { 6 int _age; 7 [DataMember] 8 [DisplayName("Age")] 9 [Description("Specifies the age of a person.")] 10 public int Age 11 { 12 get { return _age; } 13 set { _age = value; } 14 } 15 16
除了添加了新的屬性,又添加了構造函數,這個構造函數通過一個基類的實例來初始化子類對象,更 有意思的是,又添加了個類型轉換方法,這個方法將子類轉換為父類,這樣做的原因是,有時候我們想獲 得一個通用服務協議中定義的狀態對象的序列化對象,而不包括子類所擴展的其他信息,假如僅僅使用 CLR所默認的隱式類型轉換,我們得到的對象仍舊是子類對象,因此,要獲得一個基類型的序列化對象, 只能顯示的實現一個基類對象,而不是隱式轉換。
1 internal ExtensionState(generic.GenericState state) 2 { 3 this.FirstName = state.FirstName; 4 this.LastName = state.LastName; 5 this.Age = -1; 6 } 7 internal generic.GenericState ToGenericState() 8 { 9 10 generic.GenericState gen = new generic.GenericState(); 11 gen.FirstName = this.FirstName; 12 gen.LastName = this.LastName; 13 return gen; 14 } 15 16
這個實例中我們又添加了一個消息操作類型,即UpdateAge操作,假如我們沒有擴展服務狀態而只是添 加一個操作,我們可以重用通用服務協議定義的消息操作類,但是這裡我們只能重新定義一個服務操作。
1 /// <summary> 2 /// Generic Service Extension Main Operations Port 3 /// </summary> 4 [ServicePort] 5 public class GenericServiceExtensionOperations : 6 7 PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, Replace, UpdateAge> 8 { 9 10 } 11
服務實現
和前面的服務的實現方式不一樣,前面服務完全使用通用服務的協議中定義的狀態和操作,而這個服 務的狀態和主端口都使用的擴展的狀態和操作,因為主端口使用的是擴展的操作類型,所以它並不能處理 從備用端口傳來的消息,因此,和前面服務不同的是,我們還需要聲明另一個端口用來處理通用服務協議 中定義的操作,這個端口即備用端口(AlternateServicePort),在對端口的聲明上和前面的服務有所不 同,前面的服務在類(ExtensionService)層次上使用AlternateContract屬性,這裡我們用 AlternateServicePort屬性來標記備用端口,代碼如下:
1 // The state of this service is an extension of the generic service 2 private ExtensionState _state = new ExtensionState(); 3 4 // The operations port is an extension of the generic service in that it supports UPDATE as well 5 [ServicePort("/GenericServiceExtension", AllowMultipleInstances = false)] 6 private GenericServiceExtensionOperations _mainPort = new GenericServiceExtensionOperations(); 7 8 // The alternate port where we only have the generic service operations (without UPDATE) 9 [AlternateServicePort(AlternateContract = generic.Contract.Identifier)] 10 private generic.GenericServiceOperations _altPort = new generic.GenericServiceOperations (); 11
注意:
AlternateContract和AlternateServicePort是不能同時使用的哦,當你將通用服務協議所定義的操作 類用作主端口時,那麼使用AlternateContract屬性來標記這個服務,而如果你沒有將通用服務協議定義 的操作類作為主端口,而是將它作為備用端口,那就要在端口聲明時為其標記AlternateServicePort屬性 。
剩下的事就是為消息定義處理方法,因為這個服務中有兩個端口,每個端口都有自己的消息類型,即 主端口和備用端口各自接收自己所定義的消息,代碼如下:
這段是處理主端口傳來的消息:
1 /// <summary> 2 /// Get Handler 3 /// </summary> 4 /// <param name="get"></param> 5 /// <returns></returns> 6 [ServiceHandler(ServiceHandlerBehavior.Concurrent)] 7 8 public virtual IEnumerator<ITask> GetHandler(Get get) 9 10 { 11 get.ResponsePort.Post(_state); 12 yield break; 13 14 } 15 16 17 18 /// <summary> 19 /// Replace Handler 20 /// </summary> 21 /// <param name="replace"></param> 22 /// <returns></returns> 23 [ServiceHandler(ServiceHandlerBehavior.Exclusive)] 24 public virtual IEnumerator<ITask> ReplaceHandler(Replace replace) 25 26 { 27 28 _state = replace.Body; 29 replace.ResponsePort.Post(DefaultReplaceResponseType.Instance); 30 yield break; 31 32 } 33 34 35 36 /// <summary> 37 /// Update Handler 38 /// </summary> 39 /// <param name="update"></param> 40 /// <returns></returns> 41 [ServiceHandler(ServiceHandlerBehavior.Exclusive)] 42 43 public virtual IEnumerator<ITask> UpdateAgeHandler(UpdateAge update) 44 45 { 46 47 _state.Age = update.Body.Age; 48 update.ResponsePort.Post(DefaultUpdateResponseType.Instance); 49 yield break; 50 51 } 52 53 54 55
這段是處理從備用端口傳來的消息的代碼,這裡使用了服務狀態中所定義的類型轉換方法 ToGenericState,從子類對象中抽離出了父類對象,這樣的好處是,提高了這個服務的通用性,因為父類 對象是在通用服務協議中定義的,是大家所共知的,是規范,和其他服務交互的時候通常是傳遞的這個通 用的狀態對象的哦,彼此之間耦合就降了下來,這和面向對象中的多態類似哦。
同時要注意這些處理方法的屬性中多了一個參數PortFieldName,這個參數用來告訴DSS運行時環境將 這個處理方法附加到名字為_altPort的備用端口(Alternate Port)上。
沒有使用這個參數的處理方法會被DSS環境會附加到主端口(Service Port)。
/// <summary>
/// Get Handler
/// </summary>
/// <param name="get"></param>
/// <returns></returns>
[ServiceHandler(ServiceHandlerBehavior.Concurrent, PortFieldName = "_altPort")]
public virtual IEnumerator<ITask> GenericGetHandler(generic.Get get)
{
get.ResponsePort.Post(_state.ToGenericState());
yield break;
}
/// <summary>
/// Replace Handler
/// </summary>
/// <param name="replace"></param>
/// <returns></returns>
[ServiceHandler(ServiceHandlerBehavior.Exclusive, PortFieldName = "_altPort")]
public virtual IEnumerator<ITask> GenericReplaceHandler(generic.Replace replace)
{
_state = new ExtensionState(replace.Body);
replace.ResponsePort.Post(DefaultReplaceResponseType.Instance);
yield break;
}
運行服務
運行服務後,從服務目錄同樣可以看到兩個服務(genericserviceextension and genericserviceextension/servicetutorial8),但是和前面實現通用協議的服務不同,這兩個服務有不 同的狀態。
圖2-運行服務後在目錄中出現兩個服務(genericserviceextension and genericserviceextension/servicetutorial8)這兩個服有不同的狀態和操作,包括相同的狀態實例。
第一個服務(genericserviceextension)包含了擴展的狀態和操作,第二個服務 (genericserviceextension/servicetutorial8)包含通用的狀態和操作。
圖3-第二個服務(genericserviceextension/servicetutorial8)包含通用的狀態和操作,這樣其他 的服務可以把這個服務視為通用服務而與之通信了。
第四步:實現一個多例服務(multi-headed service)
最後一個服務實例在(MultiFunctionService.cs and MultiFunctionServiceTypes.cs)定義,MSDN 中稱它為(multi-headed service),我們稱他為多例服務,即在一個服務中實現多個狀態和端口,端口 之間是相互獨立並行執行,就好像一個服務中包含了多個獨立的服務實例,從外部看來,這些服務都是相 互獨立的,他們可能共享了一個資源或者相互之間有比較高的耦合度,當開發多例服務時,還是需要考慮 下把這些服務分開來實現是不是更好呢?因為分開實現會有更大的靈活性。
多例服務的實現和前面所說的服務實現方法是一樣的,他可能會有多個操作端口,一個主端口(main port),其他的是備用端口(alternate port),並且還要為這些端口分別實現消息處理方法。
和前面服務不同的是,多例服務包括多個狀態對象實例,這些狀態實例看起來並不相關。
1 // The first state of this multi headed service
2 private AddressState _address = new AddressState();
3
4 // The operations port used for the first service
5 [ServicePort("/AddressService", AllowMultipleInstances = false)]
6 private AddressServiceOperations _mainPort = new AddressServiceOperations();
7
8 // The second state of this multi headed service
9 private generic.GenericState _name = new generic.GenericState();
10
11 // The alternate port used for the second service
12 [AlternateServicePort(AlternateContract = generic.Contract.Identifier)]
13 private generic.GenericServiceOperations _altPort = new generic.GenericServiceOperations ();
14
對每個端口分別實現消息處理方法,每個端口的處理方法各自維護自己的狀態對象。
1 // Service handlers for service one
2
3 /// <summary>
4 /// Get Handler
5 /// </summary>
6 /// <param name="get"></param>
7 /// <returns></returns>
8 [ServiceHandler(ServiceHandlerBehavior.Concurrent)]
9 public virtual IEnumerator<ITask> GetAddressHandler(Get get)
10 {
11 get.ResponsePort.Post(_address);
12 yield break;
13 }
14
15 /// <summary>
16 /// Replace Handler
17 /// </summary>
18 /// <param name="replace"></param>
19 /// <returns></returns>
20 [ServiceHandler(ServiceHandlerBehavior.Exclusive)]
21 public virtual IEnumerator<ITask> ReplaceAddressHandler(Replace replace)
22 {
23 _address = replace.Body;
24 replace.ResponsePort.Post(DefaultReplaceResponseType.Instance);
25 yield break;
26 }
27
28 // Service handlers for service two
29
30 /// <summary>
31 /// Get Handler
32 /// </summary>
33 /// <param name="get"></param>
34 /// <returns></returns>
35 [ServiceHandler(ServiceHandlerBehavior.Concurrent, PortFieldName = "_altPort")]
36 public virtual IEnumerator<ITask> GetNameHandler(generic.Get get)
37 {
38 get.ResponsePort.Post(_name);
39 yield break;
40 }
41
42 /// <summary>
43 /// Replace Handler
44 /// </summary>
45 /// <param name="replace"></param>
46 /// <returns></returns>
47 [ServiceHandler(ServiceHandlerBehavior.Exclusive, PortFieldName = "_altPort")]
48 public virtual IEnumerator<ITask> ReplaceNameHandler(generic.Replace replace)
49 {
50 _name = replace.Body;
51 replace.ResponsePort.Post(DefaultReplaceResponseType.Instance);
52 yield break;
53 }
運行服務
運行服務後,服務列表中顯示兩個實例(addressservice and addressservice/servicetutorial8.) 但是,兩個服務實例的返回狀態是不同的。
這是MRDS的服務實例中的第九個,講的的是如何實行或擴展一個通用服務協議,在實際應用中是很有 用的,因為我們不必自己定義通信的消息類型和操作類型了,直接使用微軟自己定義的協議,這樣這些服 務在VPL裡就可以變得通用了,微軟自己定義了一套協議,呼呼,他希望大家都去實現,微軟眼光很長遠 ,想在機器人領域續寫PC領域的輝煌哦……
噢耶!Your Potential!Our Passion!