このトピックでは、WCF トランスポートを使用して大きなメッセージを送受信するとき、またはオーケストレーションでメッセージを読み込むときに、ストリーミング パターンを使用してメッセージ のメモリ占有領域を最小限に抑えるための推奨事項を示します。
オーケストレーションでコードを使用してメッセージの内容を読み取る場合は、XmlDocument 変数を使用しないでください。 XmlDocument 変数にメッセージを読み込むと、特に大きなメッセージに対して大きなオーバーヘッドが発生します。 このオーバーヘッドは、メモリ内構造を構築するためのメモリ使用量と処理に関するものです。 XmlDocument インスタンスを使用すると、ドキュメント オブジェクト モジュール (DOM) のオブジェクト グラフを作成するために、メッセージの内容全体が強制的にメモリに読み込まれます。 このクラスのインスタンスによって使用されるメモリの合計量は、実際のメッセージ サイズの約 10 倍になります。 XmlDocument 変数にメッセージを読み込むときに必要なメモリ 占有領域の詳細については、「 第 9 章 - XML パフォーマンスの向上」を参照してください。
このトピックの残りの部分では、XmlDocument 変数にメッセージを読み込む必要のないメッセージの内容を読み取るための代替メソッドを提供します。
WCF トランスポートで大きなメッセージを送受信するときにストリーミングを使用する
WCF トランスポートで大きなメッセージを送受信する場合は、WCF-Custom または WCF-CustomIsolated アダプターを使用し、 transferMode = Streamed オプションをサポートするバインドの種類 (次のバインドなど) を使用して構成します。
basicHttpBinding + BasicHttpBindingElement、transferMode = Streamed
netTcpBinding + NetTcpBindingElement、transferMode = Streamed
customBinding + HttpTransportElement、transferMode = Streamed
customBinding +ConnectionOrientedTransportElement、transferMode = Streamed
transferMode = Streamed オプションをサポートするバインディングと共に WCF-Custom または WCF-CustomIsolated アダプターを選択すると、必要に応じて大きなメッセージのファイル システムへのストリーミングが実装され、メモリ不足の問題が軽減されます。
ストリーミングを使用して、オーケストレーションでメッセージを読み込むときに必要なメモリ占有領域を最小限に抑える
次の手法では、メッセージをオーケストレーションに読み込むときにメッセージのメモリ占有領域を最小限に抑える方法について説明します。
XLANGMessage 変数を使用してメッセージまたはメッセージ部分の内容を処理する
オーケストレーションから .NET クラス ライブラリにメッセージを渡す場合は、このトピックで前述した理由により、メッセージを XmlDocument 変数として渡さないでください。代わりに XLANGMessage 変数を使用してください。 次の手法は、XLANGMessage 変数を使用してメッセージまたはメッセージ部分を読み取るメソッドを示しています。
XMLReader を使用してメッセージを処理 する - XmlReader インスタンスを使用してメッセージを処理するには、メッセージを XLANGMessage として .NET コードに渡し、XmlReader を使用してパーツコンテンツを取得します。
public void ProcessMessage(XLANGMessage message) { try { using (XmlReader reader = message[0].RetrieveAs(typeof(XmlReader)) as XmlReader) if (reader != null) { ... } } finally { message.Dispose(); } }
StreamReader を使用してメッセージの内容を文字列に取得 する - オーケストレーションでの XmlDocument の一般的な用途の 1 つは、XmlDocument.OuterXml() を使用して XML 文字列としてメッセージを取得することです。 次のコード例は、XLANGMessage 変数を使用してメッセージを文字列として取得する代替メソッドを示しています。
public static string MessageToString(XLANGMessage message) { string strResults; try { using (Stream stream = message[0].RetrieveAs(typeof(Stream)) as Stream) { using (StreamReader reader = new StreamReader(stream)) { strResults = reader.ReadToEnd(); } } } finally { message.Dispose(); } return strResults; }
単純な .NET メッセージの内容を文字列に取得 する - メッセージの型が単純な .NET 型の場合は、その型としてメッセージを取得できます。 たとえば、メッセージを文字列として取得するには、メッセージを XLANGMessage として .NET コードに渡し、パーツの内容を文字列として取得します。
public void ProcessMessage(XLANGMessage message) { try { string content = message[0].RetrieveAs(typeof(string)) as string; if (!string.IsNullOrEmpty(content)) { ... } } finally { message.Dispose(); } }
メッセージの内容をストリームに取得 する - メッセージをストリームとして取得するには、メッセージを XLANGMessage として .NET コードに渡し、パーツの内容をストリームとして取得します。
public Stream ProcessRequestReturnStream(XLANGMessage message, int bufferSize, int thresholdSize) { ... try { using (VirtualStream virtualStream = new VirtualStream(bufferSize, thresholdSize)) { using (Stream partStream = (Stream)message[0].RetrieveAs(typeof(Stream))) //Note that when calling this code, if the XmlDocument is quite large, keeping it in a memory with a MemoryStream may have an adverse effect on performance. //In this case, it may be worthwhile to consider an approach that uses a VirtualStream + ReadonlySeekableStream to buffer it to the file system, if its size is bigger than the thresholdSize parameter. //Keep in mind that: // - If the message size is smaller than the threshold size, the VirtualStream class buffers the stream to a MemoryStream. // - If the message size is bigger than the threshold size, the VirtualStream class buffers the stream to a temporary file. using (ReadOnlySeekableStream readOnlySeekableStream = new ReadOnlySeekableStream(partStream, virtualStream, bufferSize)) { using (XmlReader reader = XmlReader.Create(readOnlySeekableStream)) { } } } } } catch (Exception ex) { } finally { message.Dispose(); } return stream; }
メッセージの内容を .NET オブジェクトに取得 する - .NET オブジェクトとしてメッセージを取得するには、メッセージを XLANGMessage として .NET コードに渡し、.NET クラスのインスタンスとしてパーツの内容を取得します。 これは、Visual Studio 2010 によって提供される XML スキーマ定義ツール (Xsd.exe) ツールを使用して、メッセージの Xml スキーマから作成します。
注
この手法は、メッセージが小さい場合にのみ有効です。 それ以外の場合、この方法では、実際のメッセージを .NET オブジェクトに逆シリアル化するために大きなオーバーヘッドが発生する可能性があります。
public void ProcessMessage(XLANGMessage message) { try { Request request = message[0].RetrieveAs(typeof(Request)) as Request; if (request != null) { ... } } finally { message.Dispose(); } }
注
.NET コードから戻る前に XLANGMessage パラメーターによって公開される Dispose() メソッドの使用は、ループ シナリオや実行時間の長いオーケストレーションで特に重要です。このオーケストレーションは、時間の経過と同時に解放することなく XLANGMessage オブジェクトのインスタンスを蓄積できます。 メッセージに対して Dispose() メソッドをこのように呼び出すことについて詳しくは、BizTalk Server のドキュメント内の 「XLANGMessage として表されるメッセージ」 を参照してください。 このトピックでは、IStreamFactory を使用して、ストリームベースのアプローチを使用してユーザー コードで XLANGMessage 変数を構築するためのベスト プラクティスについても説明します。
オーケストレーションによって呼び出されたヘルパー コンポーネント内で XLANGMessage を処理するさまざまな方法の詳細については、次のトピックを参照してください。
オーケストレーションによって呼び出されたヘルパー コンポーネント内で XLANGMessage を処理する 4 つの異なる方法パート 1 (サード パーティのブログを開く)
オーケストレーションによって呼び出されたヘルパー コンポーネント内で XLANGMessage を処理する 4 つの異なる方法パート 2 (サード パーティのブログを開く)
XPathReader と XPathCollection を使用して、オーケストレーションによって呼び出されたメソッドから XLANGMessage オブジェクトから値を抽出する
XMLDocument クラスを使用して、カスタム パイプライン コンポーネントやオーケストレーションによって呼び出されたヘルパー クラスなど、カスタム コードから XML メッセージの内容を読み取らないようにします。 XMLDocument インスタンスを使用して XML メッセージを読み込むと、メッセージ全体がメモリに読み込まれます。これは非効率的であり、メッセージの実際のサイズの最大 10 倍のメモリが必要になる場合があります。 XML メッセージの内容を読み取るより効率的な方法は、ストリーミング手法を使用して、Microsoft.BizTalk.Streaming.dll アセンブリによって提供されるストリーム クラスのいずれかで元のストリームをラップすることです。 この手法は、大きなメッセージを読み込むときに特に便利です。
XmlDocument クラスによって公開される SelectNodes メソッドと SelectSingleNode メソッドを使用する代わりに、XML ドキュメントから特定の値をプルする必要がある場合は、次のコード例に示すように、Microsoft.BizTalk.XPathReader.dll アセンブリによって提供される XPathReader クラスのインスタンスを使用します。
この例では、XPathReader と XPathCollection を使用して、オーケストレーションによって呼び出されたメソッド内の XLANGMessage オブジェクトから特定の値を抽出する方法を示します。
public static string SelectSingleNode(XLANGMessage message, string xPath) { try { if (message == null || string.IsNullOrEmpty(xPath)) { return string.Empty; } using (XmlReader reader = (XmlReader)message[0].RetrieveAs(typeof(XmlReader))) { XPathCollection xPathCollection = new XPathCollection(); XPathReader xPathReader = new XPathReader(reader, xPathCollection); xPathCollection.Add(xPath); while (xPathReader.ReadUntilMatch()) { if (xPathReader.Match(0)) { return xPathReader.ReadString(); } } } } catch (Exception ex) { ... } finally { message.Dispose(); } return string.Empty; }