2016-03-17 2 views
2

(Подобный this вопрос, однако в конечном итоге не нужно в этом случае)WCF IErrorHandler Получить сообщение запроса

Я использую реализацию IErrorHandler - создание FaultException так, чтобы клиенты к службе получить «довольно 'ошибки в случае необработанных исключений.

Я бы хотел использовать элемент в сообщении и вернуть его обратно в исключение FaultException, и я не выигрываю.

я могу получить доступ к OperationContext.Current.RequestContext.RequestMessage, и может видеть полный конверт (в том числе имущества «идентификатор сообщения» Я хочу FaultResponse содержать), используя его ToString(), однако я хотел бы либо «вручную» десериализации запрос сообщение, или даже просто прочитать тело как XML, чтобы извлечь свойство запроса, которое я ищу.

Однако я не в состоянии прочитать из сообщения, так как (я предполагаю) Послание уже где-то читал предыдущий в WCF трубопровода:

«Сообщение не может поддерживать операцию, так как это было скопировал»

Вот мой ProvideFault() реализации (я знаю XElement разбор не может работать даже в таком виде - я не сосредотачиваясь на том, что в настоящее время):

public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { 
    if (error is FaultException) 
    { 
     // Let WCF handle    
    } 
    else 
    { 
     // Extract the 'messageID' element from the request message 
     var req = OperationContext.Current.RequestContext.RequestMessage; 

     // Throws the 'has been copied' exception 
     XElement body = XElement.Parse(XElement.ReadFrom(req.GetReaderAtBodyContents()).ToString()); 

     var msgId = (string)body.Descendants("messageID").FirstOrDefault(); 

     var err = new FaultException<Foo>(new Foo 
      { 
       MessageID = msgId 
      }, "Server error"); 

     var msgFault = err.CreateMessageFault(); 
     fault = Message.CreateMessage(
      version, 
      msgFault, 
      null); 
    } } 

что т он лучший способ сделать это?

EDIT: Это не выглядит, как это можно прочитать в OperationContext в сообщение запроса во время событий IErrorHandler - предположительно, потому что это уже было прочитано ранее в конвейере WCF (создавая таким образом копию сообщения с помощью MessageBuffer не будет работать). Обходной путь к проблеме описан в принятом ответе.

+0

Я думаю, ваша проблема в том, что вы читаете объект ошибки до того, как сделать копию. Это поток ref, поэтому, если вы читаете его содержимое, это вызывает ошибку «Сообщение не может поддерживать операцию, потому что оно было скопировано». Сначала попробуйте скопировать его, например: MessageBuffer buffer = fault.CreateBufferedCopy (Int32.MaxValue); fault = buffer.CreateMessage(); Сообщение сообщениеFault = буфер.Создать сообщение(); И использовать объект messageFault вместо –

+0

Я вообще не читаю Fault (хотя в моей реальной реализации я регистрирую его) - это сообщение с запросом, которое я пытаюсь получить. Вызов CreateBufferedMessage() в сообщении Request также приводит к тому, что исключение «уже скопировано» было поднято. – Ive

+0

У меня есть. Когда точно происходит ошибка? –

ответ

4

Одним из возможных подходов является использование IDispatchMessageInspector для чтения требуемых значений из сообщения запроса и сохранения их в OperationContext до тех пор, пока вам не потребуется доступ к ним.

MSDN documentation for IDispatchMessageInspector - прямолинейный. Только не забудьте сначала создать буферную копию сообщения, прежде чем читать его

public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel, InstanceContext instanceContext) 
{ 
    var bufferedCopy = request.CreateBufferedCopy(2000000); // choose a suitable buffer size 
    request = bufferedCopy.CreateMessage();// Message for continued processing 

    var messageToRead = bufferedCopy.CreateMessage(); // message to read properties from 

    return null; 
} 

Обратитесь к этому question о том, как присоединить свои свойства OperationContext.

+0

Спасибо за указатель на использование IExtension . Я применил свое окончательное решение, чтобы использовать это. Как упоминалось в комментарии к @burning_LEGION, я в конечном итоге пошел по этому пути «хранения» свойств запроса в DispatchMessageInspector, а затем обратился к ним во время ProvideFault(). – Ive

+0

Ссылка на документацию MSDN такая же, как ссылка на OperationContext. – Finickyflame

+0

@Finickyflame спасибо, исправил ссылку. –

0

Вы должны настроить исключение и использовать его свойство, чтобы сохранить Id

public void ProvideFault(Exception error, MessageVersion version, ref Message fault) 
{ 
    var myException = error as MyException; 
    if (myException == null) 
    { 
     return;   
    } 

    var err = new FaultException<Foo>(new Foo 
     { 
      MessageID = myException.MsgId 
     }, "Server error"); 

    var msgFault = err.CreateMessageFault(); 
    fault = Message.CreateMessage(
     version, 
     msgFault, 
     null); 
} 
+0

Я пытаюсь удовлетворить необоснованные исключения; обертывание всех исключений в моем собственном исключении будет включать «вручную» захват System.Exceptions и повторное металирование моего пользовательского исключения в КАЖДОМ сервис-методе ... именно то, что я пытаюсь уйти. – Ive

+0

по какой причине? –

+0

Почему я пытаюсь уйти от повторения повторного метаданных в своих методах обслуживания N? Я бы предпочел справиться с этим в централизованном месте в общем виде и подумал, что IErrorHandler был для него местом! – Ive

1

Вы должны скопировать буфер исходного сообщения:

// Read orignal messagem 
var originalMessage = OperationContext.Current.RequestContext.RequestMessage; 

// Copy message 
MessageBuffer buffer = originalMessage.CreateBufferedCopy(Int32.MaxValue); 
//OperationContext.Current.RequestContext.RequestMessage = buffer.CreateMessage(); 

var req = buffer.CreateMessage(); 
XElement body = XElement.Parse(XElement.ReadFrom(req.GetReaderAtBodyContents()).ToString()); 

Надеется, что это помогает.

Смежные вопросы