这篇文章描述了使用三种方法来实现或扩展一个通用服务协议(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)。
Code
/// <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!