原文:http://www.rainsts.net/article.asp?id=429
WCF 一切都围绕Message进行的,下面我们来看看Message的样子。
服务端:
<ServiceContract()> _
Public Interface IService1
<OperationContract()> _
Function GetData(ByVal value As Integer) As String
End Interface
Public Class Service1
Implements IService1
Public Function GetData(ByVal value As Integer) As String Implements IService1.GetData
'打印消息
Dim msg = OperationContext.Current.RequestContext.RequestMessage
Console.WriteLine(msg.ToString)
Return String.Format("You entered: {0}", value)
End Function
End Class
Public Interface IService1
<OperationContract()> _
Function GetData(ByVal value As Integer) As String
End Interface
Public Class Service1
Implements IService1
Public Function GetData(ByVal value As Integer) As String Implements IService1.GetData
'打印消息
Dim msg = OperationContext.Current.RequestContext.RequestMessage
Console.WriteLine(msg.ToString)
Return String.Format("You entered: {0}", value)
End Function
End Class
客户端:
Dim host As System.ServiceModel.ServiceHost
host = New System.ServiceModel.ServiceHost(GetType(WcfServiceLibrary1.Service1))
host.AddServiceEndpoint(GetType(WcfServiceLibrary1.IService1), New System.ServiceModel.BasicHttpBinding, "http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/mex")
host.Open()
Console.WriteLine(host.State.ToString)
Dim factory = New System.ServiceModel.ChannelFactory(Of WcfServiceLibrary1.IService1)(New ServiceModel.BasicHttpBinding, "http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/mex")
Dim o = factory.CreateChannel
Console.WriteLine(o.GetData(10))
Console.ReadKey()
host = New System.ServiceModel.ServiceHost(GetType(WcfServiceLibrary1.Service1))
host.AddServiceEndpoint(GetType(WcfServiceLibrary1.IService1), New System.ServiceModel.BasicHttpBinding, "http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/mex")
host.Open()
Console.WriteLine(host.State.ToString)
Dim factory = New System.ServiceModel.ChannelFactory(Of WcfServiceLibrary1.IService1)(New ServiceModel.BasicHttpBinding, "http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/mex")
Dim o = factory.CreateChannel
Console.WriteLine(o.GetData(10))
Console.ReadKey()
输出消息是:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/mex</To>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IService1/GetData</Action>
</s:Header>
<s:Body>
<GetData xmlns="http://tempuri.org/">
<value>10</value>
</GetData>
</s:Body>
</s:Envelope>
<s:Header>
<To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/mex</To>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IService1/GetData</Action>
</s:Header>
<s:Body>
<GetData xmlns="http://tempuri.org/">
<value>10</value>
</GetData>
</s:Body>
</s:Envelope>
事实上我们可以直接基于Message layer 进行编程,利用operation.action捕获特定的action消息,注意action的定义是区分大小写的
服务端:
<ServiceContract()> _
Public Interface IService1
<OperationContract(action:="add", ReplyAction:="add")> _
Function GetData(ByVal msg As System.ServiceModel.Channels.Message) As System.ServiceModel.Channels.Message
End Interface
Public Class Service1
Implements IService1
Public Function GetData(ByVal msg As System.ServiceModel.Channels.Message) As System.ServiceModel.Channels.Message Implements IService1.GetData
Dim data = msg.GetBody(Of CompositeType)()
Console.WriteLine(data.StringValue)
Return System.ServiceModel.Channels.Message.CreateMessage(System.ServiceModel.Channels.MessageVersion.Soap11, "add", New CompositeType("zqonline"))
End Function
End Class
<DataContract()> _
Public Class CompositeType
Private stringValueField As String = String.Empty
Public Sub New()
End Sub
Public Sub New(ByVal str As String)
Me.StringValue = str
End Sub
<DataMember()> _
Public Property StringValue() As String
Get
Return Me.stringValueField
End Get
Set(ByVal value As String)
Me.stringValueField = value
End Set
End Property
End Class
Public Interface IService1
<OperationContract(action:="add", ReplyAction:="add")> _
Function GetData(ByVal msg As System.ServiceModel.Channels.Message) As System.ServiceModel.Channels.Message
End Interface
Public Class Service1
Implements IService1
Public Function GetData(ByVal msg As System.ServiceModel.Channels.Message) As System.ServiceModel.Channels.Message Implements IService1.GetData
Dim data = msg.GetBody(Of CompositeType)()
Console.WriteLine(data.StringValue)
Return System.ServiceModel.Channels.Message.CreateMessage(System.ServiceModel.Channels.MessageVersion.Soap11, "add", New CompositeType("zqonline"))
End Function
End Class
<DataContract()> _
Public Class CompositeType
Private stringValueField As String = String.Empty
Public Sub New()
End Sub
Public Sub New(ByVal str As String)
Me.StringValue = str
End Sub
<DataMember()> _
Public Property StringValue() As String
Get
Return Me.stringValueField
End Get
Set(ByVal value As String)
Me.stringValueField = value
End Set
End Property
End Class
客户端:
Sub Main()
Dim host As System.ServiceModel.ServiceHost
host = New System.ServiceModel.ServiceHost(GetType(WcfServiceLibrary1.Service1))
host.AddServiceEndpoint(GetType(WcfServiceLibrary1.IService1), New System.ServiceModel.BasicHttpBinding, "http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/mex")
host.Open()
Console.WriteLine(host.State.ToString)
Dim factory = New System.ServiceModel.ChannelFactory(Of System.ServiceModel.Channels.IRequestChannel)(New ServiceModel.BasicHttpBinding, "http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/mex")
Dim o = factory.CreateChannel
Dim value = New WcfServiceLibrary1.CompositeType("lily")
Dim msg = System.ServiceModel.Channels.Message.CreateMessage(System.ServiceModel.Channels.MessageVersion.Soap11, "add", value)
Dim r As System.ServiceModel.Channels.Message = o.Request(msg)
Console.WriteLine(r)
Debug.WriteLine(r)
Console.ReadKey()
End Sub
输出内容是:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header />
<s:Body>
<CompositeType xmlns="http://schemas.datacontract.org/2004/07/WcfServiceLibrary1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<StringValue>zqonline</StringValue>
</CompositeType>
</s:Body>
</s:Envelope>
<s:Header />
<s:Body>
<CompositeType xmlns="http://schemas.datacontract.org/2004/07/WcfServiceLibrary1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<StringValue>zqonline</StringValue>
</CompositeType>
</s:Body>
</s:Envelope>
正如上面所看到的,所有的调用都被转换成消息后发送。这也符合 SOA 的规范,完全隔离,清晰的边界。(调用 "m.GetBody<Data>()" 后,会导致 Message.State 变更,再次访问会出错,有关详细信息请参考 MSDN 文档。)
这个,我也不能是太理解,只是先照着做个例子,先熟中间的类库了。
我们还可以使用 MessageContractAttribute / MessageHeaderAttribute 来控制消息格式,这比 DataContractAttribute 要更加灵活。我们可以设置消息标头、消息体,包括是否对其中某些进行签名和加密处理。
不过这个我没有实现
<MessageContract()> _
Public Class CompositeType
Private boolValueField As Boolean = True
Private stringValueField As String = String.Empty
<MessageHeader()> _
Public int As Integer = 111
Public Sub New()
End Sub
Public Sub New(ByVal str As String)
Me.StringValue = str
End Sub
<MessageHeader()> _
Public Property BoolValue() As Boolean
Get
Return Me.boolValueField
End Get
Set(ByVal value As Boolean)
Me.boolValueField = value
End Set
End Property
<MessageBodyMember()> _
Public Property StringValue() As String
Get
Return Me.stringValueField
End Get
Set(ByVal value As String)
Me.stringValueField = value
End Set
End Property
End Class
生成的消息是:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header />
<s:Body>
<CompositeType xmlns="http://schemas.datacontract.org/2004/07/WcfServiceLibrary1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<BoolValue>true</BoolValue>
<StringValue>zqonline</StringValue>
<int>111</int>
</CompositeType>
</s:Body>
</s:Envelope>
并没有放在Header头。测试后发现,不能针对Message Layer层进行操作,才可以。
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:BoolValue xmlns:h="http://tempuri.org/">true</h:BoolValue>
<h:int xmlns:h="http://tempuri.org/">111</h:int>
<To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/mex</To>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">add</Action>
</s:Header>
<s:Body>
<CompositeType xmlns="http://tempuri.org/">
<StringValue>lily</StringValue>
</CompositeType>
</s:Body>
</s:Envelope>
int,BoolValue都放在了Header中了。
好了,先到这里,虽然照着把代码实现了,但是其中还是不能理解。