Я нашел этот вопрос в числе самых популярных запросов в поиске Google по "SoapExtension MSDN" (который также находит документ с примером кода в качестве главного запроса), поэтому вот несколько полезных советов для всех, кто пытается разобраться в иногда запутанные или противоречивые документы по кодированию расширений Soap.
Если вы модифицируете сериализованное сообщение (как поток), вам нужно создать и вернуть другой поток из переопределения ChainStream. В противном случае вы говорите, что ваше расширение не изменяет поток, а просто пропускает его. В примере используется MemoryStream, и это, вероятно, то, что вы должны использовать из-за странного дизайна: когда вызывается ChainStream, вы не знаете, отправляете ли вы или получаете, поэтому вы должны быть готовы обработать его в любом случае. Я думаю, что даже если вы обрабатываете его только в одном направлении, вам все равно придется обрабатывать другое направление и в любом случае копировать данные из одного потока в другой, потому что вы вставляете себя в цепочку, не зная, как это.
private Stream _transportStream; // The stream closer to the network transport.
private MemoryStream _accessStream; // The stream closer to the message access.
public override Stream ChainStream(Stream stream)
{
// You have to save these streams for later.
_transportStream = stream;
_accessStream = new MemoryStream();
return _accessStream;
}
Затем вам нужно обработать случаи AfterSerialize и BeforeDeserialize в ProcessMessage. У меня есть они, вызывающие ProcessTransmitStream (сообщение) и ProcessReceivedStream (сообщение) соответственно, чтобы помочь сохранить процесс ясным.
ProcessTransmitStream берет свой ввод из _accessStream (после первого сброса Положения этого MemoryStream в 0) и записывает свой вывод в _transportStream - что может позволить очень ограниченный доступ (без поиска и т. Д.), Поэтому я предлагаю сначала обработать в локальный Буфер MemoryStream, а затем скопировать его (после сброса его Postion в 0) в _transportStream. (Или, если вы обрабатываете его в байтовом массиве или строке, вы можете просто записать его непосредственно в _transportStream. Мой вариант использования был сжатие / декомпрессия, поэтому я склонен к обработке всего этого как потоков.)
ProcessReceivedStream берет свои входные данные из _transportStream и записывает свои выходные данные в _accessStream. В этом случае вам, вероятно, следует сначала скопировать _transportStream в локальный буфер MemoryStream (а затем сбросить позицию буфера на 0), к которой вы можете получить более удобный доступ. (Или вы можете просто прочитать весь _transportStream непосредственно в байтовый массив или другую форму, если вам это нужно.) Убедитесь, что вы сбросили _accessStream.Position = 0, прежде чем вернуться, чтобы он был готов для следующей ссылки в цепочке, чтобы читать из него.
Это для изменения сериализованного потока. Если вы не изменяете поток, вам не следует переопределять ChainStream (таким образом, вынимая свое расширение из цепочки обработки потока). Вместо этого вы выполняете обработку на этапах BeforeSerialize и / или AfterDeserialize. На этих этапах вы не изменяете потоки и не обращаетесь к ним, а вместо этого работаете с самим объектом сообщения, например, добавляете собственный SoapHeader в коллекцию message.Headers на этапе BeforeSerialize.
Сам класс SoapMessage является абстрактным, поэтому на самом деле вы получаете либо SoapClientMessage, либо SoapServerMessage. В документах говорится, что вы получаете SoapClientMessage на стороне клиента и SoapServerMessage на стороне сервера (эксперименты в отладчике должны быть в состоянии подтвердить или исправить это). Они кажутся довольно схожими с точки зрения того, к чему вы можете получить доступ, но вы должны привести к нужному, чтобы получить к нему доступ; неправильное использование не удастся, и базовый тип SoapMessage, объявленный для параметра ProcessMessage, не дает вам доступа ко всему.
Я еще не изучал атрибуты (это не будет частью того, что я кодирую), поэтому я не могу помочь с тем, как использовать эту часть.