12

私はWCFと協力して、サードパーティ企業とメッセージを交換しています。メッセージは、 ebXML仕様に一致するエンベロープで送受信する必要があります。理想的には、できるだけ多くのWCFスタックを使用し、この場合のようにすべてのアプローチを処理する1つの方法を避けたいと思います。これは、WCFのインフラストラクチャの多くを再度作成することを意味します。

私の最初の調査からわかる限り、これには独自のカスタムバインディングを作成する必要がありますが、MSDNのドキュメントで明確さを見つけるのに苦労しています。

私はこれらのそれぞれの個々の実装について多くの詳細なドキュメントを見つけることができましたが、すべてをエンドツーエンドでまとめる方法についてはほとんどありません。私が持っている本も同様にこれらのトピックについて軽いように思われますが、PeirisとMulderによる「ProWCF」ではこれについて言及されていません。

私が目指しているのは次のようなものです。

送受信されるメッセージは、次のようにフォーマットする必要があります。最初の要素の名前は実行される操作の名前であり、子要素は要求メッセージのペイロードであり、次の形式になります。

<?xml version="1.0" encoding="UTF-8"?>
<op:DoSomething xmlns:op="http://my.ebXML.schema.com" xmlns:payload="http://payload.company.com">
    <op:AnObject>
        <payload:ImportantValue>42</payload:ImportantValue>
    </op:AnObject>
</op:DoSomething>

そして、応答は次のようになります。

<?xml version="1.0" encoding="UTF-8"?>
<op:AcknowledgementResponse xmlns:op="http://my.ebXML.schema.com" xmlns:payload="http://payload.company.com">
    <op:ResponseObject>
        <payload:Ok>True</payload:Ok>
    </op:ResponseObject>
</op:AcknowledgementResponse>

メッセージはすべてXMLスキーマで記述されているため、XSD.exeを使用してこれらを厳密に型指定されたオブジェクトに変換しました。スキーマについては、https://gist.github.com/740303を参照してください。これらはサンプルスキーマであることに注意してください。クライアントの機密保持契約に違反せずに実際のスキーマを投稿することはできません(また、スキーマが巨大であるため、私も望んでいません)。

これで、サービスの実装を次のように記述できるようになります。

public class MyEndpoint : IMyEndpoint
{
    public AcknowledgementResponse DoSomething(AnObject value)
    {
        return new AcknowledgementResponse
            {
                Ok = True;
            };
    }
}

どんな助けでも大歓迎です。

4

3 に答える 3

12

ティムの答えの私の実装の詳細

私が現在働いているクライアントのためにこれを書く必要があったので、ここにも投稿したほうがよいと思いました。誰かの役に立てば幸いです。これらのアイデアのいくつかを試すために使用するサンプル クライアントとサービスを作成しました。私はそれをきれいにして、githubに追加しました。ここからダウンロードできます。

私が要求した方法で WCF を使用できるようにするには、次のことを実装する必要がありました。

  1. WCF が SOAP メッセージを予期しない
  2. 要求および応答メッセージを必要に応じて正確にフォーマットする機能
  3. 処理対象と見なされるすべてのメッセージ
  4. 受信メッセージを適切な操作にルーティングして処理する

1. SOAP メッセージを予期しないように WCF を構成する

最初のステップは、TextMessageEncoder を介して着信メッセージを取得することでした。これは、textMessageEncoding 要素に MessageVersion.None を設定したカスタム バインディングを使用することで実現されました。

  <customBinding>
    <binding name="poxMessageBinding">
      <textMessageEncoding messageVersion="None" />
      <httpTransport />
    </binding>
  </customBinding>

2. メッセージを正しくフォーマットする

メッセージ コントラクトに追加の属性を追加しないと、受信メッセージが既存の XML フォーマッタによって逆シリアル化されることが拒否されるため、メッセージ フォーマッタが必要です。これは通常は問題になりませんが、XSD.exe を使用してクライアントの ebXML スキーマを実行すると、33000 行の cs ファイルが生成されるため、これを変更する必要はまったくありませんでした。さらに、人々は将来属性を再度追加するのを忘れる可能性があるため、これによりメンテナンスも容易になることが期待されます。

カスタム フォーマッタは、受信メッセージを最初のパラメーターの型に変換し、戻り値の型を応答メッセージに変換することを想定しています。実装メソッドを検査して、コンストラクターの最初のパラメーターと戻り値の型を判別します。

public SimpleXmlFormatter(OperationDescription operationDescription)
{
    // Get the request message type
    var parameters = operationDescription.SyncMethod.GetParameters();
    if (parameters.Length != 1)
        throw new InvalidDataContractException(
"The SimpleXmlFormatter will only work with a single parameter for an operation which is the type of the incoming message contract.");
    _requestMessageType = parameters[0].ParameterType;

    // Get the response message type
    _responseMessageType = operationDescription.SyncMethod.ReturnType;
}

次に、単純に XmlSerializer を使用してデータをシリアル化および逆シリアル化します。私にとって興味深いのは、カスタム BodyWriter を使用してオブジェクトを Message オブジェクトにシリアライズしたことです。以下は、サービスシリアライザーの実装の一部です。クライアントの実装は逆です。

public void DeserializeRequest(Message message, object[] parameters)
{
    var serializer = new XmlSerializer(_requestMessageType);
    parameters[0] = serializer.Deserialize(message.GetReaderAtBodyContents());
}

public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
    return Message.CreateMessage(MessageVersion.None, _responseMessageType.Name,
                                 new SerializingBodyWriter(_responseMessageType, result));
}

private class SerializingBodyWriter : BodyWriter
{
    private readonly Type _typeToSerialize;
    private readonly object _objectToEncode;

    public SerializingBodyWriter(Type typeToSerialize, object objectToEncode) : base(false)
    {
        _typeToSerialize = typeToSerialize;
        _objectToEncode = objectToEncode;
    }

    protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
    {
        writer.WriteStartDocument();
        var serializer = new XmlSerializer(_typeToSerialize);
        serializer.Serialize(writer, _objectToEncode);
        writer.WriteEndDocument();
    }
}

3. すべての着信メッセージを処理する

すべての受信メッセージを処理できるように WCF に指示するには、endpointDispatcher の ContractFilter プロパティを MatchAllMessageFilter のインスタンスに設定する必要がありました。これは、エンドポイントの動作構成からこれを示すスニペットです。

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
    endpointDispatcher.ContractFilter = new MatchAllMessageFilter();
    // Do more config ...
}

4. 正しい操作の選択

これは、IDispatchOperationSelector を実装するクラスを作成することによって実現されました。SelectOperation メソッドで、受信メッセージを検査します。1. ルート要素の名前空間がサービス コントラクトの名前空間と同じであることのサニティ チェック 2. ルート要素の名前 (名前空間プレフィックスを削除するために LocalName を使用していることに注意してください) )

ルート要素の名前は戻り値であり、一致する名前を持つ、またはアクション属性が一致する値を持つコントラクトの任意の操作にマップされます。

public string SelectOperation(ref Message message)
{
    var messageBuffer = message.CreateBufferedCopy(16384);

    // Determine the name of the root node of the message
    using (var copyMessage = messageBuffer.CreateMessage())
    using (var reader = copyMessage.GetReaderAtBodyContents())
    {
        // Move to the first element
        reader.MoveToContent();

        if (reader.NamespaceURI != _namespace)
            throw new InvalidOperationException(
"The namespace of the incoming message does not match the namespace of the endpoint contract.");

        // The root element name is the operation name
        var action = reader.LocalName;

        // Reset the message for subsequent processing
        message = messageBuffer.CreateMessage();

        // Return the name of the action to execute
        return action;
    }
}

すべてをまとめる

展開を容易にするために、メッセージ フォーマッタ、コントラクト フィルタ、および操作セレクタの構成を処理するエンドポイント ビヘイビアを作成しました。カスタム バインディング構成をまとめるためのバインディングを作成することもできましたが、その部分を覚えるのが難しいとは思いませんでした。

私にとって興味深い発見の 1 つは、エンドポイントの動作によって、エンドポイント内のすべての操作に対してカスタム メッセージ フォーマッタを使用するようにメッセージ フォーマッタを設定できることでした。これにより、これらを個別に構成する必要がなくなります。これは、 Microsoft のサンプルの 1 つから選びました。

役立つドキュメント リンク

これまでに見つけた最良の参考文献は、Service Station MSDN マガジンの記事 (Google "site:msdn.microsoft.com service station WCF") です。

詳細なWCF バインディング- バインディングの構成に関する非常に役立つ情報

カスタム ビヘイビアを使用した WCFの拡張 - 私がまだ見つけたディスパッチャー統合ポイントに関する最良の情報源であり、すべての統合ポイントとそれらが処理の順序で発生する場所を示すいくつかの非常に役立つ図が含まれています。

Microsoft WCF サンプル- ここには、他の場所では十分に文書化されていないものがたくさんあります。これらのいくつかのソース コードを読むと、非常に有益であることがわかりました。

于 2010-12-16T15:12:13.627 に答える
5

バインディングで何もする必要はないと思います。とにかく HTTP 経由で ebXML 形式のメッセージを送信する必要があると思いますか?

@ladislavの答えは1つのアプローチですが、メッセージエンコーダーは、達成しようとしているものよりもはるかに低いレベルで動作するように設計されていると思います. これらは基本的に、基になるストリームとの間でメッセージをエンコードする部分です (つまり、メッセージがストリーム上でバイトとしてどのように表現されるか)。

あなたがする必要があるのは、カスタム Message Formatterを実装することだと思います。特に、サードパーティにメッセージを送信したいということIClientMessageFormatterなので、実装する必要があるのはインターフェイスだけだと思います。もう 1 つのインターフェイス ( IDispatchMessageFormatter) は、サーバー側で使用されます。

また、適切な ServiceBehavior と OperationBehavior を実装してフォーマッタをスタックにインストールする必要がありますが、このためのコードは最小限になります (コードの大部分は、上記のインターフェイスの実装にあります)。

実装したら、「1 つの方法ですべてを処理する」アプローチを使用して、フォーマッタをテストおよびデバッグできます。受信したメッセージをコンソールにダンプして確認し、ebXML 応答を返すだけです。同じアプローチを使用して単体テストを構築することもできます。

于 2010-12-15T09:49:18.830 に答える
1

カスタム メッセージ形式の場合、 Custom MessageEncoderが必要です。MSDNには、カスタム エンコーダーの作成方法の例が含まれています。Reflectorを使用すると、いくつかのエンコーダーの実装が見つかるので、その記述方法を学ぶことができます。

MessageVersion.None で TextMessageEncoder を使用しようとするとどうなるかを確認することもできます (試したことはありません)。

于 2010-12-14T12:11:12.347 に答える