MRS實(shí)現(xiàn)和擴(kuò)展一個(gè)Service Contract
這個(gè)實(shí)例由實(shí)現(xiàn),可以在下面的目錄中找到這個(gè)項(xiàng)目:
Samples"ServiceTutorials"Tutorial9"CSharp |
創(chuàng)建項(xiàng)目
由于項(xiàng)目需要擴(kuò)展一個(gè)通用服務(wù)協(xié)議(generic service contract),所以項(xiàng)目中需要引用Service Tutorial 8項(xiàng)目生成的dll文件,在Service Tutorial 9項(xiàng)目你會(huì)看到它實(shí)現(xiàn)了三個(gè)個(gè)服務(wù),在一個(gè)項(xiàng)目中實(shí)現(xiàn)多個(gè)服務(wù)的話(huà),要確保這些服務(wù)的命名空間是不同的哦。
第二步:實(shí)現(xiàn)一個(gè)通用服務(wù)協(xié)議
第一個(gè)服務(wù)實(shí)例是GenericServiceImplementation,實(shí)現(xiàn)這個(gè)服務(wù)的文件是GenericServiceImplementation.cs 和 GenericServiceImplementationTypes.cs,這個(gè)服務(wù)僅僅是實(shí)現(xiàn)了一個(gè)通用服務(wù)協(xié)議。
服務(wù)類(lèi)型聲明(Service Type Declarations):
因?yàn)樵谶@個(gè)服務(wù)中使用了通用服務(wù)協(xié)議,因此它不需要我們?nèi)ザx他的狀態(tài)和操作,這些在通用服務(wù)協(xié)議已經(jīng)定義好了,看看ServiceTutorial8就知道了,但是它還是需要一個(gè)協(xié)議標(biāo)識(shí)(Contract identifier),因?yàn)樾枰眠@個(gè)標(biāo)識(shí)來(lái)找到這個(gè)服務(wù),服務(wù)標(biāo)識(shí)定義如下:
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)
在項(xiàng)目中引用通用服務(wù)協(xié)議代理程序集(generic contract's proxy assembly),一定要引用代理程序集,即*Proxy.dll,這個(gè)文件可以在msrs安裝根目錄下的bin文件夾中找到,為這個(gè)通用服務(wù)協(xié)議的命名空間定義一個(gè)別名,如下:
using generic = ServiceTutorial8.Proxy;
服務(wù)實(shí)現(xiàn)
這個(gè)服務(wù)的實(shí)現(xiàn)和其他的服務(wù)基本沒(méi)有什么區(qū)別,有兩點(diǎn)如下:
1、 服務(wù)實(shí)現(xiàn)類(lèi)需要用AlternateContract屬性標(biāo)記,用來(lái)指示在這個(gè)服務(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、 因?yàn)檫@個(gè)服務(wù)自己沒(méi)有定義狀態(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
運(yùn)行服務(wù)
運(yùn)行服務(wù)后,在瀏覽器中Service Directory可以查看到兩個(gè)服務(wù)genericserviceimplementation 和 genericserviceimplementation/servicetutorial8.因?yàn)榉?wù)沒(méi)有擴(kuò)展通用服務(wù)協(xié)議,所以這兩個(gè)服務(wù)的狀態(tài)和操作是一樣的。
圖1,運(yùn)行服務(wù),如圖紅框標(biāo)出DSS節(jié)點(diǎn)中運(yùn)行了兩個(gè)服務(wù)(genericserviceimplementation and genericserviceimplementation/servicetutorial8),兩個(gè)服務(wù)有相同的狀態(tài)和操作類(lèi)型。
第三步:擴(kuò)展一個(gè)通用服務(wù)協(xié)議
第二個(gè)服務(wù)提供了對(duì)通用服務(wù)協(xié)議的實(shí)現(xiàn),同時(shí)又添加了一個(gè)自己的狀態(tài)和消息操作,這樣做的目的是:如果服務(wù)使用者只知道通用服務(wù)協(xié)議所定義狀態(tài)和操作的話(huà),他可以很容易使用這個(gè)服務(wù),并且,這個(gè)服務(wù)還允許使用者使用擴(kuò)展的狀態(tài)和操作。
服務(wù)類(lèi)型聲明
和前面實(shí)現(xiàn)的服務(wù)不同,前面的服務(wù)沒(méi)有自己的服務(wù)類(lèi)型聲明,而這個(gè)服務(wù)有自己服務(wù)類(lèi)型聲明,包括協(xié)議標(biāo)識(shí),服務(wù)狀態(tài)以及服務(wù)操作,其中服務(wù)狀態(tài)繼承自通用服務(wù)中的狀態(tài)。
協(xié)議標(biāo)識(shí)聲明如下:
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),同時(shí)有所擴(kuò)展,添加了Age屬性,繼承實(shí)現(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è)構(gòu)造函數(shù)通過(guò)一個(gè)基類(lèi)的實(shí)例來(lái)初始化子類(lèi)對(duì)象,
更有意思的是,又添加了個(gè)類(lèi)型轉(zhuǎn)換方法,這個(gè)方法將子類(lèi)轉(zhuǎn)換為父類(lèi),這樣做的原因是,
有時(shí)候我們想獲得一個(gè)通用服務(wù)協(xié)議中定義的狀態(tài)對(duì)象的序列化對(duì)象,而不包括子類(lèi)所擴(kuò)展的其他信息,
假如僅僅使用CLR所默認(rèn)的隱式類(lèi)型轉(zhuǎn)換,我們得到的對(duì)象仍舊是子類(lèi)對(duì)象,因此,要獲得一個(gè)基類(lèi)型的序列化對(duì)象,
只能顯示的實(shí)現(xiàn)一個(gè)基類(lèi)對(duì)象,而不是隱式轉(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
這個(gè)實(shí)例中我們又添加了一個(gè)消息操作類(lèi)型,即UpdateAge操作,假如我們沒(méi)有擴(kuò)展服務(wù)狀態(tài)而只是添加一個(gè)操作,我們可以重用通用服務(wù)協(xié)議定義的消息操作類(lèi),但是這里我們只能重新定義一個(gè)服務(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ù)實(shí)現(xiàn)
和前面的服務(wù)的實(shí)現(xiàn)方式不一樣,前面服務(wù)完全使用通用服務(wù)的協(xié)議中定義的狀態(tài)和操作,而這個(gè)服務(wù)的狀態(tài)和主端口都使用的擴(kuò)展的狀態(tài)和操作,因?yàn)橹鞫丝谑褂玫氖菙U(kuò)展的操作類(lèi)型,所以它并不能處理從備用端口傳來(lái)的消息,因此,和前面服務(wù)不同的是,我們還需要聲明另一個(gè)端口用來(lái)處理通用服務(wù)協(xié)議中定義的操作,這個(gè)端口即備用端口(AlternateServicePort),在對(duì)端口的聲明上和前面的服務(wù)有所不同,前面的服務(wù)在類(lèi)(ExtensionService)層次上使用AlternateContract屬性,這里我們用AlternateServicePort屬性來(lái)標(biāo)記備用端口,代碼如下:
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是不能同時(shí)使用的哦,當(dāng)你將通用服務(wù)協(xié)議所定義的操作類(lèi)用作主端口時(shí),那么使用AlternateContract屬性來(lái)標(biāo)記這個(gè)服務(wù),而如果你沒(méi)有將通用服務(wù)協(xié)議定義的操作類(lèi)作為主端口,而是將它作為備用端口,那就要在端口聲明時(shí)為其標(biāo)記AlternateServicePort屬性。
剩下的事就是為消息定義處理方法,因?yàn)檫@個(gè)服務(wù)中有兩個(gè)端口,每個(gè)端口都有自己的消息類(lèi)型,即主端口和備用端口各自接收自己所定義的消息,代碼如下:
這段是處理主端口傳來(lái)的消息:
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
這段是處理從備用端口傳來(lái)的消息的代碼,這里使用了服務(wù)狀態(tài)中所定義的類(lèi)型轉(zhuǎn)換方法ToGenericState,從子類(lèi)對(duì)象中抽離出了父類(lèi)對(duì)象,這樣的好處是,提高了這個(gè)服務(wù)的通用性,因?yàn)楦割?lèi)對(duì)象是在通用服務(wù)協(xié)議中定義的,是大家所共知的,是規(guī)范,和其他服務(wù)交互的時(shí)候通常是傳遞的這個(gè)通用的狀態(tài)對(duì)象的哦,彼此之間耦合就降了下來(lái),這和面向?qū)ο笾械亩鄳B(tài)類(lèi)似哦。
同時(shí)要注意這些處理方法的屬性中多了一個(gè)參數(shù)PortFieldName,這個(gè)參數(shù)用來(lái)告訴DSS運(yùn)行時(shí)環(huán)境將這個(gè)處理方法附加到名字為_altPort的備用端口(Alternate Port)上。
沒(méi)有使用這個(gè)參數(shù)的處理方法會(huì)被DSS環(huán)境會(huì)附加到主端口(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;
}
運(yùn)行服務(wù)
運(yùn)行服務(wù)后,從服務(wù)目錄同樣可以看到兩個(gè)服務(wù)(genericserviceextension and genericserviceextension/servicetutorial8),但是和前面實(shí)現(xiàn)通用協(xié)議的服務(wù)不同,這兩個(gè)服務(wù)有不同的狀態(tài)。
圖2-運(yùn)行服務(wù)后在目錄中出現(xiàn)兩個(gè)服務(wù)(genericserviceextension and genericserviceextension/servicetutorial8)這兩個(gè)服有不同的狀態(tài)和操作,包括相同的狀態(tài)實(shí)例。
第一個(gè)服務(wù)(genericserviceextension)包含了擴(kuò)展的狀態(tài)和操作,第二個(gè)服務(wù)(genericserviceextension/servicetutorial8)包含通用的狀態(tài)和操作。
圖3-第二個(gè)服務(wù)(genericserviceextension/servicetutorial8)包含通用的狀態(tài)和操作,這樣其他的服務(wù)可以把這個(gè)服務(wù)視為通用服務(wù)而與之通信了。
第四步:實(shí)現(xiàn)一個(gè)多例服務(wù)(multi-headed service)
最后一個(gè)服務(wù)實(shí)例在(MultiFunctionService.cs and MultiFunctionServiceTypes.cs)定義,MSDN中稱(chēng)它為(multi-headed service),我們稱(chēng)他為多例服務(wù),即在一個(gè)服務(wù)中實(shí)現(xiàn)多個(gè)狀態(tài)和端口,端口之間是相互獨(dú)立并行執(zhí)行,就好像一個(gè)服務(wù)中包含了多個(gè)獨(dú)立的服務(wù)實(shí)例,從外部看來(lái),這些服務(wù)都是相互獨(dú)立的,他們可能共享了一個(gè)資源或者相互之間有比較高的耦合度,當(dāng)開(kāi)發(fā)多例服務(wù)時(shí),還是需要考慮下把這些服務(wù)分開(kāi)來(lái)實(shí)現(xiàn)是不是更好呢?因?yàn)榉珠_(kāi)實(shí)現(xiàn)會(huì)有更大的靈活性。
多例服務(wù)的實(shí)現(xiàn)和前面所說(shuō)的服務(wù)實(shí)現(xiàn)方法是一樣的,他可能會(huì)有多個(gè)操作端口,一個(gè)主端口(main port),其他的是備用端口(alternate port),并且還要為這些端口分別實(shí)現(xiàn)消息處理方法。
和前面服務(wù)不同的是,多例服務(wù)包括多個(gè)狀態(tài)對(duì)象實(shí)例,這些狀態(tài)實(shí)例看起來(lá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
對(duì)每個(gè)端口分別實(shí)現(xiàn)消息處理方法,每個(gè)端口的處理方法各自維護(hù)自己的狀態(tài)對(duì)象。
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 }
運(yùn)行服務(wù)
運(yùn)行服務(wù)后,服務(wù)列表中顯示兩個(gè)實(shí)例(addressservice and addressservice/servicetutorial8.)但是,兩個(gè)服務(wù)實(shí)例的返回狀態(tài)是不同的。
這是MRDS的服務(wù)實(shí)例中的第九個(gè),講的的是如何實(shí)行或擴(kuò)展一個(gè)通用服務(wù)協(xié)議,在實(shí)際應(yīng)用中是很有用的,因?yàn)槲覀儾槐刈约憾x通信的消息類(lèi)型和操作類(lèi)型了,直接使用微軟自己定義的協(xié)議,這樣這些服務(wù)在VPL里就可以變得通用了,微軟自己定義了一套協(xié)議,呼呼,他希望大家都去實(shí)現(xiàn),微軟眼光很長(zhǎng)遠(yuǎn),想在機(jī)器人領(lǐng)域續(xù)寫(xiě)PC領(lǐng)域的輝煌哦……
【編輯推薦】