这个实例由实现,可以在下面的目录中找到这个项目:
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 ///
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
引用通用协议(Referencing the Generic Contract)
在项目中引用通用服务协议代理程序集(generic contract's proxy assembly),一定要引用代理程序集,即*Proxy.dll,这个文件可以在msrs安装根目录下的bin文件夹中找到,为这个通用服务协议的命名空间定义一个别名,如下:
using generic = ServiceTutorial8.Proxy;
服务实现
这个服务的实现和其他的服务基本没有什么区别,有两点如下:
1、 服务实现类需要用AlternateContract属性标记,用来指示在这个服务中使用的是通用服务协议。
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、 因为这个服务自己没有定义状态和操作,状态和操作都在通用服务协议有所定义了,只需在服务中使用罢了。如下:
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
剩下的工作是为通用协议中的定义的消息操作(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
运行服务
运行服务后,在浏览器中Service Directory可以查看到两个服务genericserviceimplementation 和 genericserviceimplementation/servicetutorial8.因为服务没有扩展通用服务协议,所以这两个服务的状态和操作是一样的。
图1,运行服务,如图红框标出DSS节点中运行了两个服务(genericserviceimplementation and genericserviceimplementation/servicetutorial8),两个服务有相同的状态和操作类型。
第三步:扩展一个通用服务协议
第二个服务提供了对通用服务协议的实现,同时又添加了一个自己的状态和消息操作,这样做的目的是:如果服务使用者只知道通用服务协议所定义状态和操作的话,他可以很容易使用这个服务,并且,这个服务还允许使用者使用扩展的状态和操作。
服务类型声明
和前面实现的服务不同,前面的服务没有自己的服务类型声明,而这个服务有自己服务类型声明,包括协议标识,服务状态以及服务操作,其中服务状态继承自通用服务中的状态。
协议标识声明如下:
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
其中的服务状态继承自通用服务协议中的状态,同时有所扩展,添加了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 ///
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
服务实现
和前面的服务的实现方式不一样,前面服务完全使用通用服务的协议中定义的状态和操作,而这个服务的状态和主端口都使用的扩展的状态和操作,因为主端口使用的是扩展的操作类型,所以它并不能处理从备用端口传来的消息,因此,和前面服务不同的是,我们还需要声明另一个端口用来处理通用服务协议中定义的操作,这个端口即备用端口(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 ///
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
这段是处理从备用端口传来的消息的代码,这里使用了服务状态中所定义的类型转换方法ToGenericState,从子类对象中抽离出了父类对象,这样的好处是,提高了这个服务的通用性,