ティムの答えの私の実装の詳細
私が現在働いているクライアントのためにこれを書く必要があったので、ここにも投稿したほうがよいと思いました。誰かの役に立てば幸いです。これらのアイデアのいくつかを試すために使用するサンプル クライアントとサービスを作成しました。私はそれをきれいにして、githubに追加しました。ここからダウンロードできます。
私が要求した方法で WCF を使用できるようにするには、次のことを実装する必要がありました。
- WCF が SOAP メッセージを予期しない
- 要求および応答メッセージを必要に応じて正確にフォーマットする機能
- 処理対象と見なされるすべてのメッセージ
- 受信メッセージを適切な操作にルーティングして処理する
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 サンプル- ここには、他の場所では十分に文書化されていないものがたくさんあります。これらのいくつかのソース コードを読むと、非常に有益であることがわかりました。