2012-06-14 39 views
14

Tengo el mismo problema que se enumera en el siguiente hilo.Error "Este mensaje no admite la operación porque se ha leído"

WSDL first WCF server where client does not send SOAPAction

I realizado los pasos que se enumeran en el mismo hilo (también se muestra a continuación)

1) Descarga los ejemplos Microsoft WCF. Añadir los siguientes archivos a su proyecto de WF_WCF_Samples \ WCF \ extensibilidad \ interoperabilidad \ RouteByBody \ CS \ servicio

DispatchByBodyOperationSelector.cs

DispatchByBodyBehaviorAttribute.cs

2) Añadir los siguientes atributos a la interfaz (al lado de su ServiceContract)

XmlSerializerFormat 

DispatchByBodyBehavior 

3) Añadir lo siguiente a su interfaz de servicio

[OperationContract(Action = "")] 

public void DoNothing() 
{ 
} 

4) Para mi servicio, WrapperName y Wrappernamespace son nulos para todos los mensajes. Tenía que ir a DispatchByBodyBehaviorAttribute y editar ApplyDispatchBehavior() para agregar las siguientes líneas para comprobar esto:

if (qname.IsEmpty) { 
    qname = new XmlQualifiedName(operationDescription.Messages[0].Body.Parts[0].Name, operationDescription.Messages[0].Body.Parts[0].Namespace); 
} 

Ahora, estoy recibiendo un mensaje de error "Este mensaje no puede soportar la operación, ya que se ha leído". Activé el trazado y capté el trazado de la pila (abajo). Si alguien tiene alguna idea sobre cómo se puede resolver esto, le agradecería si pudiera publicar algunos comentarios. ¡Gracias por cualquier ayuda!

at System.ServiceModel.Channels.Message.GetReaderAtBodyContents() 
at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeBodyContents(Message message, Object[] parameters, Boolean isRequest) 
at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeRequest(Message message, Object[] parameters) 
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.DeserializeInputs(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet) 
at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(RequestContext request, Boolean cleanThread, OperationContext currentOperationContext) 
at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(RequestContext request, OperationContext currentOperationContext) 
at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(IAsyncResult result) 
at System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(IAsyncResult result) 
at System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result) 
at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously) 
at System.Runtime.InputQueue`1.AsyncQueueReader.Set(Item item) 
at System.Runtime.InputQueue`1.EnqueueAndDispatch(Item item, Boolean canDispatchOnThisThread) 
at System.Runtime.InputQueue`1.EnqueueAndDispatch(T item, Action dequeuedCallback, Boolean canDispatchOnThisThread) 
at System.ServiceModel.Channels.SingletonChannelAcceptor`3.Enqueue(QueueItemType item, Action dequeuedCallback, Boolean canDispatchOnThisThread) 
at System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, Action callback) 
at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContextCore(IAsyncResult result) 
at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContext(IAsyncResult result) 
at System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result) 
at System.Net.LazyAsyncResult.Complete(IntPtr userToken) 
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken) 
at System.Net.ListenerAsyncResult.WaitCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped) 
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP) 
</StackTrace> 


class DispatchByBodyElementOperationSelector : IDispatchOperationSelector 
{ 
    Dictionary<XmlQualifiedName, string> dispatchDictionary; 

    public DispatchByBodyElementOperationSelector(Dictionary<XmlQualifiedName, string> dispatchDictionary) 
    { 
     this.dispatchDictionary = dispatchDictionary;             
    } 

    #region IDispatchOperationSelector Members 

    private Message CreateMessageCopy(Message message, XmlDictionaryReader body) 
    { 
     //Message copy = Message.CreateMessage(message.Version, message.Headers.Action, body); 
     //copy.Headers.CopyHeaderFrom(message, 0); 
     //copy.Properties.CopyProperties(message.Properties); 
     //return copy;  

     MessageBuffer buffer = message.CreateBufferedCopy(Int32.MaxValue); 
     Message copy = buffer.CreateMessage(); 
     buffer.Close(); 
     copy.Headers.CopyHeaderFrom(message, 0); 
     copy.Properties.CopyProperties(message.Properties); 

     return copy; 
    } 

    public string SelectOperation(ref System.ServiceModel.Channels.Message message) 
    { 
     XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents(); 

     XmlQualifiedName lookupQName = new XmlQualifiedName(bodyReader.LocalName, bodyReader.NamespaceURI); 
     message = CreateMessageCopy(message,bodyReader); 
     if (dispatchDictionary.ContainsKey(lookupQName)) 
     { 
      return dispatchDictionary[lookupQName]; 
     } 
     else 
     { 
      return null; 
     } 
    } 

    #endregion 
} 

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)] 
sealed class DispatchByBodyBehaviorAttribute : Attribute, IContractBehavior 
{ 
    #region IContractBehavior Members 

    public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) 
    { 
     // no binding parameters need to be set here 
     return; 
    } 

    public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) 
    { 
     // this is a dispatch-side behavior which doesn't require 
     // any action on the client 
     return; 
    } 

    public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime) 
    { 
     // We iterate over the operation descriptions in the contract and 
     // record the QName of the request body child element and corresponding operation name 
     // to the dictionary to be used for dispatch  
     Dictionary<XmlQualifiedName,string> dispatchDictionary = new Dictionary<XmlQualifiedName,string>(); 
     foreach(OperationDescription operationDescription in contractDescription.Operations) 
     { 
      XmlQualifiedName qname = 
       new XmlQualifiedName(operationDescription.Messages[0].Body.WrapperName, operationDescription.Messages[0].Body.WrapperNamespace); 




      if (qname.IsEmpty) 
      { 
       qname = new XmlQualifiedName(operationDescription.Messages[0].Body.Parts[0].Name, operationDescription.Messages[0].Body.Parts[0].Namespace); 
      } 

      dispatchDictionary.Add(qname, operationDescription.Name);                 
     } 

     // Lastly, we create and assign and instance of our operation selector that 
     // gets the dispatch dictionary we've just created. 
     dispatchRuntime.OperationSelector =  
      new DispatchByBodyElementOperationSelector(dispatchDictionary); 
    } 

    public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint) 
    { 
     //  
    } 

    #endregion 
} 

Respuesta

28

Debe utilizar MessageBuffer.CreateMessage:

El cuerpo de una instancia Message sólo puede ser consumido o grabar una vez. Si desea consumir una instancia de mensaje más de una vez, debe usar la clase MessageBuffer para almacenar completamente una instancia Message completa en la memoria.

http://msdn.microsoft.com/en-us/library/system.servicemodel.channels.messagebuffer.aspx

Código de mi proyecto actual:

public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) 
{ 
    MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue); 
    reply = buffer.CreateMessage(); 
    Message m = buffer.CreateMessage(); 
    LogMessage(m, " Response => "); 
} 

Añadir ref para Message parámetro y regresar un nuevo mensaje.

private Message CreateMessageCopy(ref Message message, XmlDictionaryReader body) 
{ 
... 
    message = buffer.CreateMessage(); 
+0

Gracias por publicar el código. En mi caso, estoy reutilizando las clases de la descarga de Muestras WCF. Traté de poner el código de MessageBuffer en el método CreateMessageCopy como a continuación, pero no funcionó para mí. – Jyina

+0

He editado mi pregunta original y publicado las dos clases que estoy reutilizando de muestras de WCF. Comenté el código existente en CreateMessageCopy() y traté de crear el mensaje usando MessageBuffer. Cuando ejecuto mi cliente, está fallando en la línea. MessageBuffer buffer = message.CreateBufferedCopy (Int32.MaxValue); – Jyina

+0

Parece que el mensaje ya se ha leído antes de que llegue a esta línea. No estoy seguro de dónde debería poner el código de MessageBuffer. ¿Algunas ideas? ¡Gracias! – Jyina

4

Tuve un problema muy similar, utilizando el código de muestras WCF (RouteByBody para ser precisos) también, y era capaz de resolver de una manera diferente, así que voy a publicar aquí en caso de que ayuda a nadie .

Situación: La aplicación cliente (consumidor) funcionaría en lanzamiento, sin embargo, cuando el depurador se adjunta siempre sería un error con el error "Este mensaje no puede soportar la operación, ya que ha sido leído".

después de mucho seguimiento y registro de mensajes de WCF, la única solución que funcionó para mí resultó ser tan simple:

Mi servicio fue alojado en IIS, y con debug="true" en la sección <compilation> del web.config.

Cambiándolo a debug="false"en el servicio solucionó todos mis problemas.

+0

Pero entonces ¿cómo podremos depurar? –

+0

Esto ha funcionado para nosotros. Separar el depurador del proceso permite que lleguen los mensajes. Tener el depurador conectado daría como resultado la InvalidOperationException "El mensaje no puede admitir esta operación porque se ha leído". Además, el error solo parece ocurrir si se usa 'DispatchByBodyBehavior' para el servicio. – knittl

+0

que funcionó bien! Te amo hombres – MirlvsMaximvs

2

respuesta de Dmitry Harnitski no funciona si está depurando el servicio (Se le dará una "Este mensaje no puede soportar la operación, ya que ha sido copiado." Error.)

Esto funciona incluso en el modo de depuración:

XmlDictionaryReader GetReader(ref Message message) 
{ 
    MessageBuffer buffer = message.CreateBufferedCopy(Int32.MaxValue); 
    message = buffer.CreateMessage(); 
    newMessage = buffer.CreateMessage(); 
    XmlDictionaryReader rv = buffer.CreateMessage().GetReaderAtBodyContents(); 
    buffer.Close(); 
    return rv; 
} 

static System.ServiceModel.Channels.Message newMessage = null; 
static System.ServiceModel.Channels.Message lastMessage = null; 

public string SelectOperation(ref System.ServiceModel.Channels.Message message) 
{ 
    try 
    { 
     if(message == lastMessage) 
      message = newMessage; 

     XmlDictionaryReader bodyReader = GetReader(ref message); 

     lastMessage = message; 

     XmlQualifiedName lookupQName = new XmlQualifiedName(bodyReader.LocalName, bodyReader.NamespaceURI); 
     if (dispatchDictionary.ContainsKey(lookupQName)) 
     { 
      return dispatchDictionary[lookupQName]; 
     } 
     else 
     { 
      return null; 
     } 
    } 
    catch(Exception ex) 
    { 
     throw ex; 
    } 
} 
Cuestiones relacionadas