Share via


メッセージ クラスの使用

Message クラスは、Windows Communication Foundation (WCF) の基盤です。 クライアントとサービスの間のすべての通信は、最終的には Message インスタンスの送受信となります。

通常は、Message クラスと直接対話することはありません。 その代わりに、データ コントラクト、メッセージ コントラクト、操作コントラクトなどの WCF サービス モデル コントラクトを使用して送受信メッセージを表します。 ただし、一部の高度なシナリオでは、Message を直接使用してプログラムを作成することができます。 たとえば、次のような場合に Message クラスを使用できます。

  • .NET Framework オブジェクトをシリアル化する以外の方法で送信メッセージの内容を作成する必要がある場合 (ディスク上のファイルからメッセージを直接作成する場合など)。

  • .NET Framework オブジェクトに逆シリアル化する以外の方法で受信メッセージの内容を使用する必要がある場合 (XSLT 変換を XML の未処理コンテンツに適用する場合など)。

  • メッセージ コンテンツに関係なく、一般的な方法でメッセージを処理する必要がある場合 (メッセージをルーティングまたは転送する場合や、ルーターシステム、負荷分散システム、または発行/定期受信システムを構築する場合など)。

Message クラスを使用する前に、「データ転送のアーキテクチャの概要」に記載されている WCF のデータ転送アーキテクチャを理解してください。

Message は、一般的な目的で使用するデータ コンテナーですが、その設計は SOAP プロトコルのメッセージ設計に厳密に従っています。 SOAP と同様に、メッセージには本文とヘッダーの両方があります。 メッセージ本文には実際のペイロード データが格納され、ヘッダーには追加の名前付きデータ コンテナーが格納されます。 本文とヘッダーの読み書きルールは異なります。たとえば、ヘッダーは必ずメモリにバッファーされるので順序や回数に関係なくアクセスできますが、本文は 1 度だけ読み取ってストリーミングできます。 通常 SOAP を使用する場合、メッセージ本文は SOAP 本文にマップされ、メッセージ ヘッダーは SOAP ヘッダーにマップされます。

処理におけるメッセージ クラスの使用

Message クラスは、処理の入力パラメーター、処理の戻り値、またはその両方として使用できます。 Message を処理のどこかで使用する場合、次の制限が適用されます。

  • 処理に out パラメーターまたは ref パラメーターを含めることはできません。

  • 複数の input パラメーターを指定できません。 パラメーターがある場合は、そのパラメーターを Message 型またはメッセージ コントラクト型にする必要があります。

  • 戻り値の型は、void 型、Message 型、またはメッセージ コントラクト型にする必要があります。

有効な操作コントラクトを次のコード例に示します。

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    Message GetData();

    [OperationContract]
    void PutData(Message m);
}
<ServiceContract()> _
Public Interface IMyService
    <OperationContract()> _
    Function GetData() As Message

    <OperationContract()> _
    Sub PutData(ByVal m As Message)
End Interface

基本メッセージの作成

Message クラスには、基本メッセージを作成できる静的な CreateMessage ファクトリ メソッドが用意されています。

すべての CreateMessage オーバーロードは、MessageVersion 型のバージョン パラメーターを受け取ります。これは、メッセージに使用する SOAP や WS-Addressing のバージョンを示します。 受信メッセージと同じプロトコル バージョンを使用する場合は、IncomingMessageVersion プロパティから取得した OperationContext インスタンスの Current プロパティを使用できます。 また、ほとんどの CreateMessage オーバーロードには、メッセージで使用する SOAP アクションを示す文字列パラメーターもあります。 バージョンを None に設定すると、SOAP エンベロープの生成を無効にできます。この場合、メッセージは本文のみで構成されます。

オブジェクトからのメッセージの作成

最も基本的な CreateMessage オーバーロードは、バージョンとアクションだけを受け取り、本文が空のメッセージを作成します。 別のオーバーロードは、追加の Object パラメーターを受け取ります。このオーバーロードで作成されるメッセージは、メッセージ本文が、指定されたオブジェクトでシリアル化された表現になります。 シリアル化の既定の設定で、DataContractSerializer を使用します。 異なるシリアライザーを使用する場合、または DataContractSerializer の構成を変更する場合は、CreateMessage パラメーターも受け取る XmlObjectSerializer オーバーロードを使用します。

たとえば、メッセージにオブジェクトを返す場合は、次のコードを使用できます。

public class MyService1 : IMyService
{
    public Message GetData()
    {
        Person p = new Person();
        p.name = "John Doe";
        p.age = 42;
        MessageVersion ver = OperationContext.Current.IncomingMessageVersion;
        return Message.CreateMessage(ver, "GetDataResponse", p);
    }

    public void PutData(Message m)
    {
        // Not implemented.
    }
}
[DataContract]
public class Person
{
    [DataMember] public string name;
    [DataMember] public int age;
}
Public Class MyService1
    Implements IMyService

    Public Function GetData() As Message _
     Implements IMyService.GetData
        Dim p As New Person()
        p.name = "John Doe"
        p.age = 42
        Dim ver As MessageVersion = _
          OperationContext.Current.IncomingMessageVersion
        Return Message.CreateMessage(ver, "GetDataResponse", p)

    End Function


    Public Sub PutData(ByVal m As Message) _
    Implements IMyService.PutData
        ' Not implemented.
    End Sub
End Class
<DataContract()> _
Public Class Person
    <DataMember()> _
    Public name As String
    <DataMember()> _
    Public age As Integer
End Class

XML リーダーからのメッセージの作成

CreateMessage オーバーロードの中には、オブジェクトの代わりに XmlReader または XmlDictionaryReader を本文として受け取るものがあります。 この場合、メッセージの本文には、渡された XML リーダーから読み取られた XML が格納されます。 たとえば、次のコードは、XML ファイルから読み取られた内容を本文に持つメッセージを返します。

public class MyService2 : IMyService
{
    public Message GetData()
    {
        FileStream stream = new FileStream("myfile.xml",FileMode.Open);
        XmlDictionaryReader xdr =
               XmlDictionaryReader.CreateTextReader(stream,
                           new XmlDictionaryReaderQuotas());
        MessageVersion ver =
            OperationContext.Current.IncomingMessageVersion;
        return Message.CreateMessage(ver,"GetDataResponse",xdr);
    }

    public void PutData(Message m)
    {
        // Not implemented.
    }
}
Public Class MyService2
    Implements IMyService

    Public Function GetData() As Message Implements IMyService.GetData
        Dim stream As New FileStream("myfile.xml", FileMode.Open)
        Dim xdr As XmlDictionaryReader = _
        XmlDictionaryReader.CreateTextReader(stream, New XmlDictionaryReaderQuotas())
        Dim ver As MessageVersion = OperationContext.Current.IncomingMessageVersion
        Return Message.CreateMessage(ver, "GetDataResponse", xdr)

    End Function


    Public Sub PutData(ByVal m As Message) Implements IMyService.PutData

    End Sub
End Class

さらに、本文だけではなくメッセージ全体を表す CreateMessage または XmlReader を受け取る XmlDictionaryReader オーバーロードもあります。 これらのオーバーロードは、整数の maxSizeOfHeaders パラメーターも受け取ります。 メッセージが作成されると直ちにヘッダーがメモリに必ずバッファーされます。このパラメーターは、発生するバッファーの量を制限します。 XML が信頼できないソースから取り込まれる場合は、このパラメーターを安全な値に設定してサービス拒否攻撃の可能性を軽減することが重要です。 XML リーダーが表す、メッセージの SOAP バージョンと WS-Addressing バージョンは、バージョン パラメーターを使用して示されるバージョンと一致する必要があります。

BodyWriter によるメッセージの作成

CreateMessage オーバーロードの 1 つには、メッセージ本文を記述するための BodyWriter インスタンスを受け取るものがあります。 BodyWriter は、メッセージ本文の作成方法をカスタマイズするために派生させることのできる抽象クラスです。 独自の BodyWriter 派生クラスを作成すると、カスタマイズした方法でメッセージ本文を記述できます。 本文を書き出す処理は、BodyWriter.OnWriteBodyContents を受け取る XmlDictionaryWriter メソッドによって行われます。したがって、このメソッドをオーバーライドする必要があります。

本文ライターでは、バッファリングを行うことも、行わないようにする (ストリームする) こともできます。 バッファリングされる本文ライターでは、その内容を何度でも出力できますが、ストリームされる本文ライターの内容は 1 度しか出力できません。 IsBuffered プロパティは、本文ライターがバッファリングされるかどうかを示します。 独自の本文ライターでこれを設定するには、ブール型の BodyWriter パラメーターを受け取る isBuffered プロテクト コンストラクターを呼び出します。 本文ライターでは、バッファリングされない本文ライターから、バッファリングされた本文ライターを作成することがサポートされています。 OnCreateBufferedCopy メソッドをオーバーライドすると、この処理をカスタマイズできます。 既定では、OnWriteBodyContents から返された XML が格納されているメモリ内のバッファーが使用されます。 OnCreateBufferedCopy は、整数の maxBufferSize パラメーターを受け取ります。このメソッドをオーバーライドする場合は、この最大サイズを超えるバッファーを作成しないようにする必要があります。

BodyWriter クラスには、WriteBodyContents メソッドと CreateBufferedCopy メソッドが用意されています。基本的にこれらは、それぞれ OnWriteBodyContents メソッドと OnCreateBufferedCopy メソッドの薄いラッパー (thin wrapper) です。 これらのメソッドは、バッファリングされない本文ライターが 2 回以上アクセスされないように状態チェックを実行します。 これらのメソッドは、Message に基づいたカスタム BodyWriters 派生クラスを作成する場合にのみ直接呼び出されます。

エラー メッセージの作成

特定の CreateMessage オーバーロードを使用して SOAP エラー メッセージを作成できます。 このオーバーロードの最も基本的なものは、エラーを説明する MessageFault オブジェクトを受け取ります。 他のオーバーロードは、利便性のために用意されています。 このようなオーバーロードの 1 つは、FaultCode と理由の文字列を受け取り、その情報を MessageFault で使用して MessageFault.CreateFault を作成します。 他のオーバーロードは、詳細オブジェクトを受け取り、そのオブジェクトをエラー コードと理由と共に CreateFault に渡します。 たとえば、次の操作はエラーを返します。

public class MyService3 : IMyService
{
    public Message GetData()
    {
        FaultCode fc = new FaultCode("Receiver");
        MessageVersion ver = OperationContext.Current.IncomingMessageVersion;
            return Message.CreateMessage(ver,fc,"Bad data","GetDataResponse");
    }

    public void PutData(Message m)
    {
        // Not implemented.
    }
}
Public Class MyService3
    Implements IMyService

    Public Function GetData() As Message Implements IMyService.GetData
        Dim fc As New FaultCode("Receiver")
        Dim ver As MessageVersion = OperationContext.Current.IncomingMessageVersion
        Return Message.CreateMessage(ver, fc, "Bad data", "GetDataResponse")

    End Function


    Public Sub PutData(ByVal m As Message) Implements IMyService.PutData

    End Sub
End Class

メッセージ本文データの抽出

Message クラスでは、その本文から情報を抽出する複数の方法がサポートされています。 方法は次のカテゴリに分類されます。

  • XML ライターに 1 度に書き込まれるメッセージ本文全体の取得。 これを、"メッセージの書き込み" と呼びます。

  • メッセージ本文での XML リーダーの取得。 これにより、必要に応じてメッセージ本文に後で少しずつアクセスできます。 これを、"メッセージの読み取り" と呼びます。

  • 本文を含むメッセージ全体を、メモリ内の MessageBuffer 型のバッファーにコピーできます。 これを、"メッセージのコピー" と呼びます。

Message の本文には、アクセス方法に関係なく 1 回しかアクセスできません。 メッセージ オブジェクトには State プロパティがあります。このプロパティは Created として初期化されます。 この状態は、前の 3 つのアクセス方法により、それぞれ Written、Read、Copied に設定されます。 また、メッセージ本文の内容が不要になったときは、Close メソッドによって状態が Closed に設定されます。 メッセージ本文には、状態が Created の場合にのみアクセスできます。状態が変更された後で Created に戻す方法はありません。

メッセージの書き込み

WriteBodyContents(XmlDictionaryWriter) メソッドは、指定された Message インスタンスの本文の内容を指定された XML ライターに書き込みます。 WriteBody メソッドでも同じ処理が行われますが、本文の内容は適切なラッパー要素 (<soap:body> など) で囲まれます。 最後の WriteMessage は、ラップしている SOAP エンベロープとヘッダーを含めて、メッセージ全体を書き込みます。 SOAP が無効になっている場合 (VersionMessageVersion.None)、この 3 つのメソッドすべてによって同じ処理が行われます。つまり、メッセージ本文の内容が書き込まれます。

たとえば、次のコードは、受信メッセージの本文をファイルに書き込みます。

public class MyService4 : IMyService
{
    public void PutData(Message m)
    {
        FileStream stream = new FileStream("myfile.xml",FileMode.Create);
        XmlDictionaryWriter xdw =
            XmlDictionaryWriter.CreateTextWriter(stream);
        m.WriteBodyContents(xdw);
        xdw.Flush();
    }

    public Message GetData()
    {
        throw new NotImplementedException();
    }
}
Public Class MyService4
    Implements IMyService

    Public Sub PutData(ByVal m As Message) Implements IMyService.PutData
        Dim stream As New FileStream("myfile.xml", FileMode.Create)
        Dim xdw As XmlDictionaryWriter = XmlDictionaryWriter.CreateTextWriter(stream)
        m.WriteBodyContents(xdw)
        xdw.Flush()

    End Sub


    Public Function GetData() As Message Implements IMyService.GetData
        Throw New NotImplementedException()

    End Function
End Class

さらに、特定の SOAP 開始要素タグを書き込む 2 つのヘルパー メソッドがあります。 これらのメソッドはメッセージ本文にアクセスしないため、メッセージの状態は変わりません。 次の設定があります。

  • WriteStartBody は、本文の開始要素を書き込みます (例 : <soap:Body>)。

  • WriteStartEnvelope は、エンベロープの開始要素を書き込みます (例 : <soap:Envelope>)。

対応する終了要素タグを書き込むには、対応する XML ライターで WriteEndElement を呼び出します。 これらのメソッドが直接呼び出されることはほとんどありません。

メッセージの読み取り

メッセージ本文を読み取る主な方法は、GetReaderAtBodyContents を呼び出すことです。 これにより、メッセージ本文の読み取りに使用できる XmlDictionaryReader が取得されます。 Message の状態は、GetReaderAtBodyContents が呼び出されるとすぐに Read に移行します。返された XML リーダーの使用時に移行するのではない点に注意してください。

GetBody メソッドを使用することでも、型指定されたオブジェクトとしてメッセージ本文にアクセスできます。 このメソッドは内部的に GetReaderAtBodyContents を使用するため、メッセージの状態は Read 状態に移行します (State プロパティを参照してください)。

IsEmpty プロパティを確認することをお勧めします。このプロパティが true の場合、メッセージ本文は空であり、GetReaderAtBodyContents から InvalidOperationException がスローされます。 また、メッセージが受信メッセージ (返信など) の場合は、IsFault を確認することもできます。このプロパティは、メッセージにエラーがあるかどうかを示します。

GetBody の最も基本的なオーバーロードでは、既定の設定で構成され、DataContractSerializer クォータが無効にされた MaxItemsInObjectGraph を使用して、メッセージ本文を型のインスタンス (ジェネリック パラメーターで示される) に逆シリアル化します。 別のシリアル化エンジンを使用したり、既定以外の方法で DataContractSerializer を構成したりする場合は、GetBody パラメーターを受け取る XmlObjectSerializer オーバーロードを使用します。

たとえば、次のコードは、シリアル化された Person オブジェクトを含むメッセージ本文からデータを抽出し、その人の名前を出力します。

    public class MyService5 : IMyService
    {
        public void PutData(Message m)
        {
            Person p = m.GetBody<Person>();
            Console.WriteLine(p.name);
        }

        public Message GetData()
        {
            throw new NotImplementedException();
        }
    }
}
namespace Samples2
{
    [ServiceContract]
    public interface IMyService
    {
        [OperationContract]
        Message GetData();

        [OperationContract]
        void PutData(Message m);
    }

    [DataContract]
    public class Person
    {
        [DataMember] public string name;
        [DataMember] public int age;
    }
    Public Class MyService5
        Implements IMyService

        Public Sub PutData(ByVal m As Message) Implements IMyService.PutData
            Dim p As Person = m.GetBody(Of Person)()
            Console.WriteLine(p.name)

        End Sub


        Public Function GetData() As Message Implements IMyService.GetData
            Throw New NotImplementedException()

        End Function
    End Class
End Namespace
Namespace Samples2
    <ServiceContract()> _
    Public Interface IMyService
        <OperationContract()> _
        Function GetData() As Message

        <OperationContract()> _
        Sub PutData(ByVal m As Message)
    End Interface

    <DataContract()> _
    Public Class Person
        <DataMember()> _
        Public name As String
        <DataMember()> _
        Public age As Integer
    End Class

バッファーへのメッセージのコピー

場合によっては、メッセージ本文に 2 回以上アクセスする必要が生じます。たとえば、パブリッシャー/サブスクライバー システムの一部として、同じメッセージを複数の宛先に転送する場合です。 この場合、メッセージ全体 (本文を含む) をメモリ内にバッファーする必要があります。 これを行うには、CreateBufferedCopy(Int32) を呼び出します。 このメソッドは、最大バッファー サイズを表す整数パラメーターを受け取り、このサイズ以下のバッファーを作成します。 信頼されていないソースからメッセージを受信する場合は、これを安全な値に設定することが重要です。

バッファーは MessageBuffer インスタンスとして返されます。 バッファー内のデータには、いくつかの方法でアクセスできます。 中心となる方法は、CreateMessage を呼び出し、バッファーから Message インスタンスを作成する方法です。

バッファー内のデータにアクセスする他の方法として、IXPathNavigable クラスを実装する MessageBuffer インターフェイスを実装し、基になる XML に直接アクセスする方法があります。 一部の CreateNavigator オーバーロードを使用すると、ノード クォータで保護された System.Xml.XPath ナビゲーターを作成して、アクセス可能な XML ノード数を制限できます。 これにより、非常に長い処理時間によるサービス拒否攻撃を防止できます。 このクォータは、既定では無効です。 一部の CreateNavigator オーバーロードでは、XmlSpace 列挙体を使用して XML 内で空白を処理する方法を指定できます。既定では XmlSpace.None です。

メッセージ バッファーの内容にアクセスする最後の方法は、WriteMessage を使用して内容をストリームに書き込む方法です。

MessageBuffer を操作する手順を次の例に示します。受信メッセージを複数の受信者に転送してからファイルに記録します。 バッファリングを行わないと、メッセージの本文には 1 回しかアクセスできないため、この処理は不可能になります。

[ServiceContract]
public class ForwardingService
{
    private List<IOutputChannel> forwardingAddresses;

    [OperationContract]
    public void ForwardMessage (Message m)
    {
        //Copy the message to a buffer.
        MessageBuffer mb = m.CreateBufferedCopy(65536);

        //Forward to multiple recipients.
        foreach (IOutputChannel channel in forwardingAddresses)
        {
            Message copy = mb.CreateMessage();
            channel.Send(copy);
        }

        //Log to a file.
        FileStream stream = new FileStream("log.xml",FileMode.Append);
        mb.WriteMessage(stream);
        stream.Flush();
    }
}
<ServiceContract()> _
Public Class ForwardingService
    Private forwardingAddresses As List(Of IOutputChannel)

    <OperationContract()> _
    Public Sub ForwardMessage(ByVal m As Message)
        'Copy the message to a buffer.
        Dim mb As MessageBuffer = m.CreateBufferedCopy(65536)

        'Forward to multiple recipients.
        Dim channel As IOutputChannel
        For Each channel In forwardingAddresses
            Dim copy As Message = mb.CreateMessage()
            channel.Send(copy)
        Next channel

        'Log to a file.
        Dim stream As New FileStream("log.xml", FileMode.Append)
        mb.WriteMessage(stream)
        stream.Flush()

    End Sub
End Class

MessageBuffer クラスには、この他にも注目すべきメンバーがあります。 バッファーの内容が不要になったときは、Close メソッドを呼び出してリソースを解放できます。 BufferSize プロパティは、割り当てられたバッファーのサイズを返します。 MessageContentType プロパティは、メッセージの MIME コンテンツ タイプを返します。

デバッグのためのメッセージ本文へのアクセス

デバッグの目的では、ToString メソッドを呼び出して、メッセージの文字列表現を取得できます。 一般に、メッセージがテキスト エンコーダーでエンコードされていれば、この表現はネットワーク上でのメッセージの表示形式と一致します。ただし、XML の場合は人間にとって読みやすいように書式設定されます。 1 つの例外はメッセージ本文です。 本文は 1 度しか読み取ることができません。また、ToString はメッセージの状態を変更しません。 このため、ToString メソッドでは本文にアクセスできない場合があり、メッセージ本文がプレースホルダー (たとえば、3 つのドット "...") で代用されることがあります。 したがって、メッセージ本文の内容が重要な場合は、ToString を使用してメッセージを記録しないでください。

メッセージの他の部分へのアクセス

メッセージの本文以外の情報にアクセスするさまざまなプロパティが用意されています。 ただし、これらのプロパティは、メッセージを閉じると呼び出すことができなくなります。

  • Headers プロパティは、メッセージのヘッダーを表します。 このトピックの後の「ヘッダーの操作」セクションを参照してください。

  • Properties プロパティは、メッセージ プロパティを表します。これは、メッセージに結び付けられた名前付きデータの一部で、一般的に、メッセージ送信時に送出されません。 このトピックの後の「プロパティの操作」を参照してください。

  • Version プロパティは、メッセージに関連付けられている SOAP と WS-Addressing のバージョンを示します。SOAP が無効の場合は None を示します。

  • IsFault プロパティは、メッセージが SOAP のエラー メッセージの場合に true を返します。

  • IsEmpty プロパティは、メッセージが空の場合に true を返します。

GetBodyAttribute(String, String) メソッドを使用すると、特定の名前や名前空間で識別される本文のラッパー要素 (<soap:Body> など) の特定の属性にアクセスできます。 このような属性が見つからない場合は、null が返されます。 このメソッドは、Message の状態が Created の場合 (メッセージ本文がまだアクセスされていない場合) にのみ呼び出すことができます。

ヘッダーの操作

Message には、任意の数の名前付き XML フラグメント ("ヘッダー") を含めることができます。 通常、各フラグメントは SOAP ヘッダーにマップされます。 ヘッダーには、Headers 型の MessageHeaders プロパティを使用してアクセスします。 MessageHeaders は、MessageHeaderInfo オブジェクトのコレクションです。個々のヘッダーには、その IEnumerable インターフェイスまたはインデクサーを使用してアクセスできます。 たとえば、次のコードでは、Message のすべてのヘッダー名を表示します。

public class MyService6 : IMyService
{
    public void PutData(Message m)
    {
        foreach (MessageHeaderInfo mhi in m.Headers)
        {
            Console.WriteLine(mhi.Name);
        }
    }

    public Message GetData()
    {
        throw new NotImplementedException();
    }
}
Public Class MyService6
    Implements IMyService

    Public Sub PutData(ByVal m As Message) Implements IMyService.PutData
        Dim mhi As MessageHeaderInfo
        For Each mhi In m.Headers
            Console.WriteLine(mhi.Name)
        Next mhi

    End Sub


    Public Function GetData() As Message Implements IMyService.GetData
        Throw New NotImplementedException()

    End Function
End Class

ヘッダーの追加、削除、検索

Add メソッドを使用すると、新しいヘッダーを既存のすべてのヘッダーの最後に追加できます。 また、Insert メソッドを使用すると、ヘッダーを特定のインデックスの場所に挿入できます。 挿入されたアイテムに応じて既存のヘッダーは移動します。 ヘッダーは、インデックスに従って順序付けられ、使用できる最初のインデックスは 0 です。 さまざまな CopyHeadersFrom メソッド オーバーロードを使用して、別の MessageMessageHeaders インスタンスからヘッダーを追加できます。 一部のオーバーロードはヘッダーを 1 つだけコピーします。すべてのヘッダーをコピーする別のオーバーロードもあります。 Clear メソッドは、すべてのヘッダーを削除します。 RemoveAt メソッドは、特定のインデックスのヘッダーを 1 つ削除します (以降のすべてのヘッダーは移動されます)。 RemoveAll メソッドは、特定の名前や名前空間を持つすべてのヘッダーを削除します。

FindHeader メソッドを使用して、特定のヘッダーを取得します。 このメソッドは、検索するヘッダーの名前と名前空間を受け取り、そのインデックスを返します。 複数のヘッダーが見つかった場合は、例外がスローされます。 ヘッダーが見つからなかい場合は、-1 が返されます。

SOAP ヘッダー モデルでは、ヘッダーに Actor 値を持たせ、ヘッダーの受信者を指定できます。 最も基本的な FindHeader オーバーロードでは、メッセージの最終受信者を表すヘッダーのみを検索します。 ただし、別のオーバーロードでは、検索に含める Actor 値を指定できます。 詳細については、SOAP の仕様を参照してください。

ヘッダーを CopyTo(MessageHeaderInfo[], Int32) コレクションから MessageHeaders オブジェクトの配列にコピーする MessageHeaderInfo メソッドが用意されています。

ヘッダーの XML データにアクセスするには、GetReaderAtHeader を呼び出し、特定のヘッダー インデックスの XML リーダーを返すことができます。 ヘッダーの内容をオブジェクトに逆シリアル化する場合は、GetHeader<T>(Int32) または他のいずれかのオーバーロードを使用します。 最も基本的なオーバーロードは、既定の設定で構成された DataContractSerializer を使用してヘッダーを逆シリアル化します。 異なるシリアライザーを使用したり、異なる構成の DataContractSerializer を使用したりする場合は、XmlObjectSerializer を受け取るオーバーロードの 1 つを使用します。 インデックスの代わりに、ヘッダー名、名前空間、オプションとして Actor 値のリストを受け取るオーバーロードもあります。これは、FindHeaderGetHeader の組み合わせです。

プロパティの操作

Message インスタンスには、任意の数の任意の型の名前付きオブジェクトを含めることができます。 このコレクションは、Properties 型の MessageProperties プロパティを使用してアクセスできます。 コレクションは、IDictionary<TKey,TValue> インターフェイスを実装しており、String から Object へのマッピングの機能を果たします。 通常、プロパティ値は、ネットワーク上のメッセージのどの部分にも直接マッピングされず、さまざまなメッセージ処理のヒントを WCF チャネル スタックのさまざまなチャネルまたは CopyTo(MessageHeaderInfo[], Int32) サービス フレームワークに提供します。 例については、「データ転送のアーキテクチャの概要」を参照してください。

Message クラスからの継承

CreateMessage を使用して作成された組み込みのメッセージ型がユーザーの要件を満たさない場合は、Message クラスから派生するクラスを作成します。

メッセージ本文の内容の定義

メッセージ本文内のデータにアクセスする主な手法は、書き込み、読み取り、バッファーへのコピーの 3 つです。 これらの操作は最終的に、OnWriteBodyContents の派生クラスで、OnGetReaderAtBodyContents メソッド、OnCreateBufferedCopy メソッド、または Message メソッドをそれぞれ呼び出すことになります。 Message 基本クラスでは、これらのメソッドは Message インスタンスごとに 1 つしか呼び出されないようになっており、そのメソッドが 2 回以上呼び出されることもありません。 また、この基本クラスは、閉じられているメッセージでメソッドが呼び出されないことも保証します。 実装の中でメッセージ状態を追跡する必要はありません。

OnWriteBodyContents は抽象メソッドであり、実装が必要です。 メッセージの本文内容を定義する最も基本的な方法は、このメソッドを使用して書き込むことです。 たとえば、次のメッセージには 1 から 20 までの 100,000 個の乱数が含まれています。

public class RandomMessage : Message
{
    override protected  void  OnWriteBodyContents(XmlDictionaryWriter writer)
    {
        Random r = new Random();
        for (int i = 0; i <100000; i++)
        {
            writer.WriteStartElement("number");
            writer.WriteValue(r.Next(1,20));
            writer.WriteEndElement();
        }
    }
    //code omitted…
Public Class RandomMessage
    Inherits Message

    Protected Overrides Sub OnWriteBodyContents( _
            ByVal writer As XmlDictionaryWriter)
        Dim r As New Random()
        Dim i As Integer
        For i = 0 To 99999
            writer.WriteStartElement("number")
            writer.WriteValue(r.Next(1, 20))
            writer.WriteEndElement()
        Next i

    End Sub
    ' Code omitted.

OnGetReaderAtBodyContents() メソッドと OnCreateBufferedCopy メソッドには、ほとんどの場合に有効な既定の実装があります。 既定の実装は、OnWriteBodyContents を呼び出し、結果をバッファーに格納して、格納後のバッファーを操作します。 ただし、これが十分ではない場合もあります。 前の例では、メッセージを読み込むと 100,000 個の XML 要素がバッファーに格納されることになりますが、これは望ましくありません。 OnGetReaderAtBodyContents() をオーバーライドすると、乱数を提供するカスタムの XmlDictionaryReader 派生クラスを返すことができます。 これで、OnWriteBodyContents をオーバーライドして、OnGetReaderAtBodyContents() メソッドから返されたリーダーを使用できます。この例を次に示します。

    public override MessageHeaders Headers
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }

    public override MessageProperties Properties
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }

    public override MessageVersion Version
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }
}

public class RandomMessage2 : Message
{
    override protected XmlDictionaryReader OnGetReaderAtBodyContents()
    {
    return new RandomNumbersXmlReader();
    }

    override protected void OnWriteBodyContents(XmlDictionaryWriter writer)
    {
        XmlDictionaryReader xdr = OnGetReaderAtBodyContents();
        writer.WriteNode(xdr, true);
    }
    public override MessageHeaders Headers
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }

    public override MessageProperties Properties
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }

    public override MessageVersion Version
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }
}

public class RandomNumbersXmlReader : XmlDictionaryReader
{
    //code to serve up 100000 random numbers in XML form omitted…

    Public Overrides ReadOnly Property Headers() As MessageHeaders
        Get
            Throw New Exception("The method or operation is not implemented.")
        End Get
    End Property

    Public Overrides ReadOnly Property Properties() As MessageProperties
        Get
            Throw New Exception("The method or operation is not implemented.")
        End Get
    End Property

    Public Overrides ReadOnly Property Version() As MessageVersion
        Get
            Throw New Exception("The method or operation is not implemented.")
        End Get
    End Property
End Class

Public Class RandomMessage2
    Inherits Message

    Protected Overrides Function OnGetReaderAtBodyContents() As XmlDictionaryReader
        Return New RandomNumbersXmlReader()

    End Function


    Protected Overrides Sub OnWriteBodyContents(ByVal writer As XmlDictionaryWriter)
        Dim xdr As XmlDictionaryReader = OnGetReaderAtBodyContents()
        writer.WriteNode(xdr, True)

    End Sub

    Public Overrides ReadOnly Property Headers() As MessageHeaders
        Get
            Throw New Exception("The method or operation is not implemented.")
        End Get
    End Property

    Public Overrides ReadOnly Property Properties() As MessageProperties
        Get
            Throw New Exception("The method or operation is not implemented.")
        End Get
    End Property

    Public Overrides ReadOnly Property Version() As MessageVersion
        Get
            Throw New Exception("The method or operation is not implemented.")
        End Get
    End Property
End Class

Public Class RandomNumbersXmlReader
    Inherits XmlDictionaryReader
    'code to serve up 100000 random numbers in XML form omitted

同様に OnCreateBufferedCopy をオーバーライドして、独自の MessageBuffer 派生クラスを返すこともできます。

メッセージの派生クラスでは、メッセージ本文の内容を提供するだけでなく、VersionHeadersProperties の各プロパティをオーバーライドする必要もあります。

メッセージのコピーを作成した場合、作成したコピーでは、元のメッセージのメッセージ ヘッダーが使用されます。

オーバーライド可能なその他のメンバー

OnWriteStartEnvelopeOnWriteStartHeaders、および OnWriteStartBody メソッドをオーバーライドして、SOAP エンベロープ、SOAP ヘッダー、SOAP 本文要素の開始タグの書き込み方法を指定できます。通常、これらは、<soap:Envelope><soap:Header>、および <soap:Body> に対応します。 一般的に、Version プロパティで None が返された場合、これらのメソッドで何も書き込まないでください。

Note

OnGetReaderAtBodyContents の既定の実装では、OnWriteStartEnvelope を呼び出して結果をバッファーに格納する前に、OnWriteStartBodyOnWriteBodyContents を呼び出します。 ヘッダーは書き込まれません。

OnWriteMessage メソッドをオーバーライドして、さまざまな部分からメッセージ全体を構築する方法を変更します。 OnWriteMessage メソッドは、WriteMessage や既定の OnCreateBufferedCopy 実装から呼び出されます。 WriteMessage をオーバーライドすることは、ベスト プラクティスではありません。 適切な On メソッド (たとえば、OnWriteStartEnvelopeOnWriteStartHeaders、および OnWriteBodyContents) をオーバーライドすることをお勧めします。

OnBodyToString をオーバーライドして、デバッグ中のメッセージ本文の表現方法をオーバーライドします。 既定では、3 つのドット ("...") で表されます。 このメソッドは、メッセージの状態が Closed 以外のときに複数回呼び出すことができます。 このメソッドを実装すると、1 度だけ実行する必要のある処理 (転送のみのストリームからの読み取りなど) は発生しません。

OnGetBodyAttribute メソッドをオーバーライドして、SOAP 本文要素の属性にアクセスできるようにします。 このメソッドは何度でも呼び出すことができますが、Message 基本型は、メッセージの状態が Created の時にのみ呼び出されることを保証します。 実装の中で状態をチェックする必要はありません。 既定の実装は、本文要素に属性がないことを示す null を常に返します。

メッセージ本文が必要でなくなったときに、Message オブジェクトに特別なクリーンアップを実行する必要がある場合は、OnClose をオーバーライドします。 既定の実装では、何も行われません。

IsEmpty プロパティと IsFault プロパティは、オーバーライド可能です。 既定では、両方とも false を返します。