MRS實現(xiàn)和擴展一個Service Contract
這個實例由實現(xiàn),可以在下面的目錄中找到這個項目:
Samples"ServiceTutorials"Tutorial9"CSharp |
創(chuàng)建項目
由于項目需要擴展一個通用服務(wù)協(xié)議(generic service contract),所以項目中需要引用Service Tutorial 8項目生成的dll文件,在Service Tutorial 9項目你會看到它實現(xiàn)了三個個服務(wù),在一個項目中實現(xiàn)多個服務(wù)的話,要確保這些服務(wù)的命名空間是不同的哦。
第二步:實現(xiàn)一個通用服務(wù)協(xié)議
第一個服務(wù)實例是GenericServiceImplementation,實現(xiàn)這個服務(wù)的文件是GenericServiceImplementation.cs 和 GenericServiceImplementationTypes.cs,這個服務(wù)僅僅是實現(xiàn)了一個通用服務(wù)協(xié)議。
服務(wù)類型聲明(Service Type Declarations):
因為在這個服務(wù)中使用了通用服務(wù)協(xié)議,因此它不需要我們?nèi)ザx他的狀態(tài)和操作,這些在通用服務(wù)協(xié)議已經(jīng)定義好了,看看ServiceTutorial8就知道了,但是它還是需要一個協(xié)議標識(Contract identifier),因為需要用這個標識來找到這個服務(wù),服務(wù)標識定義如下:
1 ///
2 /// Generic Service Implementation Contract Identifier
3 ///
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
引用通用協(xié)議(Referencing the Generic Contract)
在項目中引用通用服務(wù)協(xié)議代理程序集(generic contract's proxy assembly),一定要引用代理程序集,即*Proxy.dll,這個文件可以在msrs安裝根目錄下的bin文件夾中找到,為這個通用服務(wù)協(xié)議的命名空間定義一個別名,如下:
using generic = ServiceTutorial8.Proxy;
服務(wù)實現(xiàn)
這個服務(wù)的實現(xiàn)和其他的服務(wù)基本沒有什么區(qū)別,有兩點如下:
1、 服務(wù)實現(xiàn)類需要用AlternateContract屬性標記,用來指示在這個服務(wù)中使用的是通用服務(wù)協(xié)議。
1 ///
2 /// This service provides an implementation of the generic service
3 ///
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、 因為這個服務(wù)自己沒有定義狀態(tài)和操作,狀態(tài)和操作都在通用服務(wù)協(xié)議有所定義了,只需在服務(wù)中使用罷了。如下:
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 ///
10 /// Default Service Constructor
11 ///
12 public ImplementationService(DsspServiceCreationPort creationPort)
13 :
14 base(creationPort)
15 {
16 }
17
剩下的工作是為通用協(xié)議中的定義的消息操作(message operations)添加處理方法(handler),如下代碼定義了Get操作的處理方法:
1 /// Get Handler
2 ///
3 ///
4 ///
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
運行服務(wù)
運行服務(wù)后,在瀏覽器中Service Directory可以查看到兩個服務(wù)genericserviceimplementation 和 genericserviceimplementation/servicetutorial8.因為服務(wù)沒有擴展通用服務(wù)協(xié)議,所以這兩個服務(wù)的狀態(tài)和操作是一樣的。
圖1,運行服務(wù),如圖紅框標出DSS節(jié)點中運行了兩個服務(wù)(genericserviceimplementation and genericserviceimplementation/servicetutorial8),兩個服務(wù)有相同的狀態(tài)和操作類型。
第三步:擴展一個通用服務(wù)協(xié)議
第二個服務(wù)提供了對通用服務(wù)協(xié)議的實現(xiàn),同時又添加了一個自己的狀態(tài)和消息操作,這樣做的目的是:如果服務(wù)使用者只知道通用服務(wù)協(xié)議所定義狀態(tài)和操作的話,他可以很容易使用這個服務(wù),并且,這個服務(wù)還允許使用者使用擴展的狀態(tài)和操作。
服務(wù)類型聲明
和前面實現(xiàn)的服務(wù)不同,前面的服務(wù)沒有自己的服務(wù)類型聲明,而這個服務(wù)有自己服務(wù)類型聲明,包括協(xié)議標識,服務(wù)狀態(tài)以及服務(wù)操作,其中服務(wù)狀態(tài)繼承自通用服務(wù)中的狀態(tài)。
協(xié)議標識聲明如下:
1 ///
2 /// Generic Service Extension Contract Identifier
3 /// ///
4 public static class Contract
5 {
6 public const string Identifier = "http://schemas.tempuri.org/2007/08/servicetutorial9/genericservice/extension.html";
7 }
8
其中的服務(wù)狀態(tài)繼承自通用服務(wù)協(xié)議中的狀態(tài),同時有所擴展,添加了Age屬性,繼承實現(xiàn)如下:
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
除了添加了新的屬性,又添加了構(gòu)造函數(shù),這個構(gòu)造函數(shù)通過一個基類的實例來初始化子類對象,
更有意思的是,又添加了個類型轉(zhuǎn)換方法,這個方法將子類轉(zhuǎn)換為父類,這樣做的原因是,
有時候我們想獲得一個通用服務(wù)協(xié)議中定義的狀態(tài)對象的序列化對象,而不包括子類所擴展的其他信息,
假如僅僅使用CLR所默認的隱式類型轉(zhuǎn)換,我們得到的對象仍舊是子類對象,因此,要獲得一個基類型的序列化對象,
只能顯示的實現(xiàn)一個基類對象,而不是隱式轉(zhuǎn)換。
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操作,假如我們沒有擴展服務(wù)狀態(tài)而只是添加一個操作,我們可以重用通用服務(wù)協(xié)議定義的消息操作類,但是這里我們只能重新定義一個服務(wù)操作。
1 ///
2 /// Generic Service Extension Main Operations Port
3 ///
4 [ServicePort]
5 public class GenericServiceExtensionOperations :
6
7 PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, Replace, UpdateAge>
8 {
9
10 }
11
服務(wù)實現(xiàn)
和前面的服務(wù)的實現(xiàn)方式不一樣,前面服務(wù)完全使用通用服務(wù)的協(xié)議中定義的狀態(tài)和操作,而這個服務(wù)的狀態(tài)和主端口都使用的擴展的狀態(tài)和操作,因為主端口使用的是擴展的操作類型,所以它并不能處理從備用端口傳來的消息,因此,和前面服務(wù)不同的是,我們還需要聲明另一個端口用來處理通用服務(wù)協(xié)議中定義的操作,這個端口即備用端口(AlternateServicePort),在對端口的聲明上和前面的服務(wù)有所不同,前面的服務(wù)在類(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是不能同時使用的哦,當你將通用服務(wù)協(xié)議所定義的操作類用作主端口時,那么使用AlternateContract屬性來標記這個服務(wù),而如果你沒有將通用服務(wù)協(xié)議定義的操作類作為主端口,而是將它作為備用端口,那就要在端口聲明時為其標記AlternateServicePort屬性。
剩下的事就是為消息定義處理方法,因為這個服務(wù)中有兩個端口,每個端口都有自己的消息類型,即主端口和備用端口各自接收自己所定義的消息,代碼如下:
這段是處理主端口傳來的消息:
1 ///
2 /// Get Handler
3 ///
4 ///
5 ///
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 ///
19 /// Replace Handler
20 ///
21 ///
22 ///
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 ///
37 /// Update Handler
38 ///
39 ///
40 ///
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
這段是處理從備用端口傳來的消息的代碼,這里使用了服務(wù)狀態(tài)中所定義的類型轉(zhuǎn)換方法ToGenericState,從子類對象中抽離出了父類對象,這樣的好處是,提高了這個服務(wù)的通用性,因為父類對象是在通用服務(wù)協(xié)議中定義的,是大家所共知的,是規(guī)范,和其他服務(wù)交互的時候通常是傳遞的這個通用的狀態(tài)對象的哦,彼此之間耦合就降了下來,這和面向?qū)ο笾械亩鄳B(tài)類似哦。
同時要注意這些處理方法的屬性中多了一個參數(shù)PortFieldName,這個參數(shù)用來告訴DSS運行時環(huán)境將這個處理方法附加到名字為_altPort的備用端口(Alternate Port)上。
沒有使用這個參數(shù)的處理方法會被DSS環(huán)境會附加到主端口(Service Port)。
///
/// Get Handler
///
///
///
[ServiceHandler(ServiceHandlerBehavior.Concurrent, PortFieldName = "_altPort")]
public virtual IEnumerator<ITask> GenericGetHandler(generic.Get get)
{
get.ResponsePort.Post(_state.ToGenericState());
yield break;
}
///
/// Replace Handler
///
///
///
[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;
}
運行服務(wù)
運行服務(wù)后,從服務(wù)目錄同樣可以看到兩個服務(wù)(genericserviceextension and genericserviceextension/servicetutorial8),但是和前面實現(xiàn)通用協(xié)議的服務(wù)不同,這兩個服務(wù)有不同的狀態(tài)。
圖2-運行服務(wù)后在目錄中出現(xiàn)兩個服務(wù)(genericserviceextension and genericserviceextension/servicetutorial8)這兩個服有不同的狀態(tài)和操作,包括相同的狀態(tài)實例。
第一個服務(wù)(genericserviceextension)包含了擴展的狀態(tài)和操作,第二個服務(wù)(genericserviceextension/servicetutorial8)包含通用的狀態(tài)和操作。
圖3-第二個服務(wù)(genericserviceextension/servicetutorial8)包含通用的狀態(tài)和操作,這樣其他的服務(wù)可以把這個服務(wù)視為通用服務(wù)而與之通信了。
第四步:實現(xiàn)一個多例服務(wù)(multi-headed service)
最后一個服務(wù)實例在(MultiFunctionService.cs and MultiFunctionServiceTypes.cs)定義,MSDN中稱它為(multi-headed service),我們稱他為多例服務(wù),即在一個服務(wù)中實現(xiàn)多個狀態(tài)和端口,端口之間是相互獨立并行執(zhí)行,就好像一個服務(wù)中包含了多個獨立的服務(wù)實例,從外部看來,這些服務(wù)都是相互獨立的,他們可能共享了一個資源或者相互之間有比較高的耦合度,當開發(fā)多例服務(wù)時,還是需要考慮下把這些服務(wù)分開來實現(xiàn)是不是更好呢?因為分開實現(xiàn)會有更大的靈活性。
多例服務(wù)的實現(xiàn)和前面所說的服務(wù)實現(xiàn)方法是一樣的,他可能會有多個操作端口,一個主端口(main port),其他的是備用端口(alternate port),并且還要為這些端口分別實現(xiàn)消息處理方法。
和前面服務(wù)不同的是,多例服務(wù)包括多個狀態(tài)對象實例,這些狀態(tài)實例看起來并不相關(guān)。
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
對每個端口分別實現(xiàn)消息處理方法,每個端口的處理方法各自維護自己的狀態(tài)對象。
1 // Service handlers for service one
2
3 ///
4 /// Get Handler
5 ///
6 ///
7 ///
8 [ServiceHandler(ServiceHandlerBehavior.Concurrent)]
9 public virtual IEnumerator<ITask> GetAddressHandler(Get get)
10 {
11 get.ResponsePort.Post(_address);
12 yield break;
13 }
14
15 ///
16 /// Replace Handler
17 ///
18 ///
19 ///
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 ///
31 /// Get Handler
32 ///
33 ///
34 ///
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 ///
43 /// Replace Handler
44 ///
45 ///
46 ///
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 }
運行服務(wù)
運行服務(wù)后,服務(wù)列表中顯示兩個實例(addressservice and addressservice/servicetutorial8.)但是,兩個服務(wù)實例的返回狀態(tài)是不同的。
這是MRDS的服務(wù)實例中的第九個,講的的是如何實行或擴展一個通用服務(wù)協(xié)議,在實際應(yīng)用中是很有用的,因為我們不必自己定義通信的消息類型和操作類型了,直接使用微軟自己定義的協(xié)議,這樣這些服務(wù)在VPL里就可以變得通用了,微軟自己定義了一套協(xié)議,呼呼,他希望大家都去實現(xiàn),微軟眼光很長遠,想在機器人領(lǐng)域續(xù)寫PC領(lǐng)域的輝煌哦……
【編輯推薦】