none
AfterReceiveReply Problem (MessageInspector)

    Question

  • Hi,

    I am using the message inspector and it works. My message(request) is correct. In Fiddler I get the correct answer. But in my code where I should get the response back I get nothing. When I modify the 'AfterReceiveReply' and try to get the body of the message I get the correct response. But it is not forwarded to my request function as the response. If I try to add a Value out of the reply into a Property it does not work. The value of my property is changing to nothing as soon I step out of the Sub AfterReceiveReply(ByRef reply As System.ServiceModel.Channels.Message, ByVal correlationState As Object)

    Public Sub AfterReceiveReply(ByRef reply As System.ServiceModel.Channels.Message, ByVal correlationState As Object) Implements System.ServiceModel.Dispatcher.IClientMessageInspector.AfterReceiveReply
    
        '/// Antwort auslesen Token in String lesen
        Dim memStream1 As New MemoryStream()
        Dim quotas1 As New XmlDictionaryReaderQuotas()
        Dim xdr1 As XmlDictionaryReader = XmlDictionaryReader.CreateBinaryReader(memStream1, quotas1)
        xdr1 = reply.GetReaderAtBodyContents()
        Dim XMLDoc As New XmlDocument
        XMLDoc.Load(xdr1)
        Dim BarmProp As New Common.Class1
        For Each node As XmlElement In XMLDoc.ChildNodes
          BarmProp.BiproToken = node.InnerText
    
        Next

    Lothar

    Wednesday, February 23, 2011 12:33 PM

Answers

  • The variable BarmProp which you declare in the function is local to the function AfterReceiveReply, so when the function returns, its value is discarded. If you want to save something inside that function to use later, you need to store it in a variable / property / field declared outside the scope of the function. In the (modified) example below, I added a field to the class, and saved the value for the nodes in that field. After the call is returned, the caller can then access that value.

    And I don't understand when you say that 'if I leave Sub AfterReceiveReply empty i don't get the response back'; in the example below, if AfterReceiveReply is completely commented out, you'll still have the result of the function (the line where the caller prints proxy.Add(...)).

      Public Class Post_3396ffba_4e33_40f2_b8e6_9fcb7f0b3a88
    
        Private Shared allNodes As List(Of String) = New List(Of String)
    
        <ServiceContract()> Public Interface ITest
          <OperationContract()> Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
        End Interface
    
        Public Class Service
          Implements ITest
    
          Public Function Add(ByVal x As Integer, ByVal y As Integer) As Integer Implements ITest.Add
            Return x + y
          End Function
        End Class
    
        Public Class MyInspector
          Implements IEndpointBehavior
          Implements IClientMessageInspector
    
          Public Sub AddBindingParameters(ByVal endpoint As ServiceEndpoint, ByVal bindingParameters As BindingParameterCollection) Implements IEndpointBehavior.AddBindingParameters
    
          End Sub
    
          Public Sub ApplyClientBehavior(ByVal endpoint As ServiceEndpoint, ByVal clientRuntime As ClientRuntime) Implements IEndpointBehavior.ApplyClientBehavior
            clientRuntime.MessageInspectors.Add(Me)
          End Sub
    
          Public Sub ApplyDispatchBehavior(ByVal endpoint As ServiceEndpoint, ByVal endpointDispatcher As EndpointDispatcher) Implements IEndpointBehavior.ApplyDispatchBehavior
    
          End Sub
    
          Public Sub Validate(ByVal endpoint As ServiceEndpoint) Implements IEndpointBehavior.Validate
    
          End Sub
    
          Public Sub AfterReceiveReply(ByRef reply As Message, ByVal correlationState As Object) Implements IClientMessageInspector.AfterReceiveReply
            Dim xdr As XmlDictionaryReader = reply.GetReaderAtBodyContents() ' reply has been read; need to re-create it later
            Dim XMLDoc As XmlDocument = New XmlDocument()
            XMLDoc.Load(xdr)
    
            'Saving the information in a "global" variable
            For Each node As XmlElement In XMLDoc.ChildNodes
              allNodes.Add(node.InnerText)
            Next
    
            'Recreating the reply
            Dim memStream As New MemoryStream
            Dim writer As XmlDictionaryWriter = XmlDictionaryWriter.CreateBinaryWriter(memStream)
            XMLDoc.WriteTo(writer)
            writer.Flush()
            memStream.Position = 0
            Dim reader As XmlDictionaryReader = XmlDictionaryReader.CreateBinaryReader(memStream, XmlDictionaryReaderQuotas.Max)
            Dim newReply As Message = Message.CreateMessage(reply.Version, Nothing, reader)
            newReply.Headers.CopyHeadersFrom(reply)
            newReply.Properties.CopyProperties(reply.Properties)
    
            'Now adding a new property into the "reply"
            newReply.Properties.Add(name:="MyNewProperty", [property]:="Value for my property")
    
            reply = newReply
          End Sub
    
          Public Function BeforeSendRequest(ByRef request As Message, ByVal channel As IClientChannel) As Object Implements IClientMessageInspector.BeforeSendRequest
            Return Nothing
          End Function
        End Class
    
        Public Shared Sub Test()
          Dim baseAddress As String = "http://" + Environment.MachineName+":8000/Service"
          Dim host As New ServiceHost(GetType(Service), New Uri(baseAddress))
          host.AddServiceEndpoint(GetType(ITest), New BasicHttpBinding(), "")
          Dim smb As ServiceMetadataBehavior = New ServiceMetadataBehavior
          smb.HttpGetEnabled = True
          host.Description.Behaviors.Add(smb)
          host.Open()
    
          Dim factory = New ChannelFactory(Of ITest)(New BasicHttpBinding(), New EndpointAddress(baseAddress))
          factory.Endpoint.Behaviors.Add(New MyInspector())
          Dim proxy = factory.CreateChannel()
          Using New OperationContextScope(CType(proxy, IContextChannel))
            Console.WriteLine(proxy.Add(1234, 5434))
            Console.WriteLine("All response properties:")
            For Each prop In OperationContext.Current.IncomingMessageProperties
              Console.WriteLine("{0}: {1}", prop.Key, prop.Value)
            Next
          End Using
    
          Console.WriteLine("All nodes read in AfterReceiveReply:")
          For Each node In allNodes
            Console.WriteLine(" " + node)
          Next
          Console.WriteLine("Press ENTER to close")
          Console.ReadLine()
        End Sub
      End Class
    
    
    • Marked as answer by LOMECO Friday, February 25, 2011 7:29 AM
    Wednesday, February 23, 2011 10:54 PM

All replies

  • Instances of the Message class are "read-once" objects - once you consume it, you cannot use it again. Once you consume (in your example, calling GetReaderAtBodyContents), you'll need to recreate the message so that it can be passed to the caller. The code below shows how the message can be recreated, and modified after the fact.

      Public Class Post_3396ffba_4e33_40f2_b8e6_9fcb7f0b3a88
    
        <ServiceContract()> Public Interface ITest
          <OperationContract()> Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
        End Interface
    
        Public Class Service
          Implements ITest
    
          Public Function Add(ByVal x As Integer, ByVal y As Integer) As Integer Implements ITest.Add
            Return x + y
          End Function
        End Class
    
        Public Class MyInspector
          Implements IEndpointBehavior
          Implements IClientMessageInspector
    
          Public Sub AddBindingParameters(ByVal endpoint As ServiceEndpoint, ByVal bindingParameters As BindingParameterCollection) Implements IEndpointBehavior.AddBindingParameters
    
          End Sub
    
          Public Sub ApplyClientBehavior(ByVal endpoint As ServiceEndpoint, ByVal clientRuntime As ClientRuntime) Implements IEndpointBehavior.ApplyClientBehavior
            clientRuntime.MessageInspectors.Add(Me)
          End Sub
    
          Public Sub ApplyDispatchBehavior(ByVal endpoint As ServiceEndpoint, ByVal endpointDispatcher As EndpointDispatcher) Implements IEndpointBehavior.ApplyDispatchBehavior
    
          End Sub
    
          Public Sub Validate(ByVal endpoint As ServiceEndpoint) Implements IEndpointBehavior.Validate
    
          End Sub
    
          Public Sub AfterReceiveReply(ByRef reply As Message, ByVal correlationState As Object) Implements IClientMessageInspector.AfterReceiveReply
            Dim xdr As XmlDictionaryReader = reply.GetReaderAtBodyContents() ' reply has been read; need to re-create it later
            Dim XMLDoc As XmlDocument = New XmlDocument()
            XMLDoc.Load(xdr)
    
            'Recreating the reply
            Dim memStream As New MemoryStream
            Dim writer As XmlDictionaryWriter = XmlDictionaryWriter.CreateBinaryWriter(memStream)
            XMLDoc.WriteTo(writer)
            writer.Flush()
            memStream.Position = 0
            Dim reader As XmlDictionaryReader = XmlDictionaryReader.CreateBinaryReader(memStream, XmlDictionaryReaderQuotas.Max)
            Dim newReply As Message = Message.CreateMessage(reply.Version, Nothing, reader)
            newReply.Headers.CopyHeadersFrom(reply)
            newReply.Properties.CopyProperties(reply.Properties)
    
            'Now adding a new property into the "reply"
            newReply.Properties.Add(name:="MyNewProperty", [property]:="Value for my property")
    
            reply = newReply
          End Sub
    
          Public Function BeforeSendRequest(ByRef request As Message, ByVal channel As IClientChannel) As Object Implements IClientMessageInspector.BeforeSendRequest
            Return Nothing
          End Function
        End Class
    
        Public Shared Sub Test()
          Dim baseAddress As String = "http://" + Environment.MachineName+":8000/Service"
          Dim host As New ServiceHost(GetType(Service), New Uri(baseAddress))
          host.AddServiceEndpoint(GetType(ITest), New BasicHttpBinding(), "")
          Dim smb As ServiceMetadataBehavior = New ServiceMetadataBehavior
          smb.HttpGetEnabled = True
          host.Description.Behaviors.Add(smb)
          host.Open()
    
          Dim factory = New ChannelFactory(Of ITest)(New BasicHttpBinding(), New EndpointAddress(baseAddress))
          factory.Endpoint.Behaviors.Add(New MyInspector())
          Dim proxy = factory.CreateChannel()
          Using New OperationContextScope(CType(proxy, IContextChannel))
            Console.WriteLine(proxy.Add(1234, 5434))
            Console.WriteLine("All response properties:")
            For Each prop In OperationContext.Current.IncomingMessageProperties
              Console.WriteLine("{0}: {1}", prop.Key, prop.Value)
            Next
          End Using
          Console.WriteLine("Press ENTER to close")
          Console.ReadLine()
        End Sub
      End Class
    
    
    Wednesday, February 23, 2011 5:24 PM
  • Hi Carlos,

    thanks for the answer, but I don't need to modfy the reply, I just want tu use the reply. There is a Token inside of the reply body and I need this Token,

    I can see it when I use the : reply.GetReaderAtBodyContents() but how can I put this into an external property and read it later???

    If I use :

    Dim BarmProp As New Common.Class1
        For Each node As XmlElement In XMLDoc.ChildNodes
          BarmProp.BiproToken = node.InnerText

        Next

    and map the token inside a new property inside of class1 property BarmProb.BiproToken it is nothing when I tried to use it.

    You are saying that if I don't consume the message class it will go to the caller but that is not true.

    If I leave Sub  AfterReceiveReply empty(no line of code) I don't get the response back to my Function, where I could read it normaly.

    I am confused, I have a response but I can't us it.

    Lothar

    Wednesday, February 23, 2011 8:57 PM
  • The variable BarmProp which you declare in the function is local to the function AfterReceiveReply, so when the function returns, its value is discarded. If you want to save something inside that function to use later, you need to store it in a variable / property / field declared outside the scope of the function. In the (modified) example below, I added a field to the class, and saved the value for the nodes in that field. After the call is returned, the caller can then access that value.

    And I don't understand when you say that 'if I leave Sub AfterReceiveReply empty i don't get the response back'; in the example below, if AfterReceiveReply is completely commented out, you'll still have the result of the function (the line where the caller prints proxy.Add(...)).

      Public Class Post_3396ffba_4e33_40f2_b8e6_9fcb7f0b3a88
    
        Private Shared allNodes As List(Of String) = New List(Of String)
    
        <ServiceContract()> Public Interface ITest
          <OperationContract()> Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
        End Interface
    
        Public Class Service
          Implements ITest
    
          Public Function Add(ByVal x As Integer, ByVal y As Integer) As Integer Implements ITest.Add
            Return x + y
          End Function
        End Class
    
        Public Class MyInspector
          Implements IEndpointBehavior
          Implements IClientMessageInspector
    
          Public Sub AddBindingParameters(ByVal endpoint As ServiceEndpoint, ByVal bindingParameters As BindingParameterCollection) Implements IEndpointBehavior.AddBindingParameters
    
          End Sub
    
          Public Sub ApplyClientBehavior(ByVal endpoint As ServiceEndpoint, ByVal clientRuntime As ClientRuntime) Implements IEndpointBehavior.ApplyClientBehavior
            clientRuntime.MessageInspectors.Add(Me)
          End Sub
    
          Public Sub ApplyDispatchBehavior(ByVal endpoint As ServiceEndpoint, ByVal endpointDispatcher As EndpointDispatcher) Implements IEndpointBehavior.ApplyDispatchBehavior
    
          End Sub
    
          Public Sub Validate(ByVal endpoint As ServiceEndpoint) Implements IEndpointBehavior.Validate
    
          End Sub
    
          Public Sub AfterReceiveReply(ByRef reply As Message, ByVal correlationState As Object) Implements IClientMessageInspector.AfterReceiveReply
            Dim xdr As XmlDictionaryReader = reply.GetReaderAtBodyContents() ' reply has been read; need to re-create it later
            Dim XMLDoc As XmlDocument = New XmlDocument()
            XMLDoc.Load(xdr)
    
            'Saving the information in a "global" variable
            For Each node As XmlElement In XMLDoc.ChildNodes
              allNodes.Add(node.InnerText)
            Next
    
            'Recreating the reply
            Dim memStream As New MemoryStream
            Dim writer As XmlDictionaryWriter = XmlDictionaryWriter.CreateBinaryWriter(memStream)
            XMLDoc.WriteTo(writer)
            writer.Flush()
            memStream.Position = 0
            Dim reader As XmlDictionaryReader = XmlDictionaryReader.CreateBinaryReader(memStream, XmlDictionaryReaderQuotas.Max)
            Dim newReply As Message = Message.CreateMessage(reply.Version, Nothing, reader)
            newReply.Headers.CopyHeadersFrom(reply)
            newReply.Properties.CopyProperties(reply.Properties)
    
            'Now adding a new property into the "reply"
            newReply.Properties.Add(name:="MyNewProperty", [property]:="Value for my property")
    
            reply = newReply
          End Sub
    
          Public Function BeforeSendRequest(ByRef request As Message, ByVal channel As IClientChannel) As Object Implements IClientMessageInspector.BeforeSendRequest
            Return Nothing
          End Function
        End Class
    
        Public Shared Sub Test()
          Dim baseAddress As String = "http://" + Environment.MachineName+":8000/Service"
          Dim host As New ServiceHost(GetType(Service), New Uri(baseAddress))
          host.AddServiceEndpoint(GetType(ITest), New BasicHttpBinding(), "")
          Dim smb As ServiceMetadataBehavior = New ServiceMetadataBehavior
          smb.HttpGetEnabled = True
          host.Description.Behaviors.Add(smb)
          host.Open()
    
          Dim factory = New ChannelFactory(Of ITest)(New BasicHttpBinding(), New EndpointAddress(baseAddress))
          factory.Endpoint.Behaviors.Add(New MyInspector())
          Dim proxy = factory.CreateChannel()
          Using New OperationContextScope(CType(proxy, IContextChannel))
            Console.WriteLine(proxy.Add(1234, 5434))
            Console.WriteLine("All response properties:")
            For Each prop In OperationContext.Current.IncomingMessageProperties
              Console.WriteLine("{0}: {1}", prop.Key, prop.Value)
            Next
          End Using
    
          Console.WriteLine("All nodes read in AfterReceiveReply:")
          For Each node In allNodes
            Console.WriteLine(" " + node)
          Next
          Console.WriteLine("Press ENTER to close")
          Console.ReadLine()
        End Sub
      End Class
    
    
    • Marked as answer by LOMECO Friday, February 25, 2011 7:29 AM
    Wednesday, February 23, 2011 10:54 PM
  • Tanks Carlos,

    that works perfect many thanks!!!!

     

    Lothar

    Friday, February 25, 2011 7:29 AM