按本文項目分派
AdvancedDispatchByBody 範例示範如何實作將傳入訊息指派給作業的替代演算法。
根據預設,伺服器模型發送器會根據訊息的 WS-Addressing "Action" 標頭或 HTTP SOAP 要求的相同資訊,對傳入訊息選取適當的處理方法。
有些未遵循 WS-I Basic Profile 1.1 方針的 SOAP 1.1 Web 服務堆疊,不會根據 Action URI 分派訊息,而是根據 SOAP 本文內之第一個項目的 XML 限定名稱來分派。 同樣地,這些堆疊的用戶端可能會傳送具有空白或 SOAP 1.1 規格允許之任意 HTTP SoapAction 標頭的訊息。
若要變更訊息分派至方法的方法,範例會在 IDispatchOperationSelector 上實作 DispatchByBodyElementOperationSelector
擴充性介面。 這個類別會根據訊息本文的第一個項目選取作業。
類別建構函式預期會以 XmlQualifiedName
和字串組填入字典,因此限定名稱表示 SOAP 本文之第一個子系的名稱,而字串表示相符的作業名稱。 defaultOperationName
是作業的名稱,而此作業會接收不符合此字典的所有訊息:
class DispatchByBodyElementOperationSelector : IDispatchOperationSelector
{
Dictionary<XmlQualifiedName, string> dispatchDictionary;
string defaultOperationName;
public DispatchByBodyElementOperationSelector(Dictionary<XmlQualifiedName,string> dispatchDictionary, string defaultOperationName)
{
this.dispatchDictionary = dispatchDictionary;
this.defaultOperationName = defaultOperationName;
}
}
IDispatchOperationSelector 實作的建置上則非常簡單,因為在介面上只有一個方法:SelectOperation。 此方法的工作為檢查傳入的訊息,並針對目前的端點,傳回等於服務合約之方法名稱的字串。
在此範例中,作業選取器會使用 XmlDictionaryReader,取得傳入訊息本文的 GetReaderAtBodyContents。 這個方法已經將讀取器放置在訊息本文的第一個子系上,因此便可取得目前項目的名稱和命名空間 URI,並將這些項目結合至 XmlQualifiedName
,然後使用結合項目查閱作業選取器持有之字典的對應作業。
public string SelectOperation(ref System.ServiceModel.Channels.Message message)
{
XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
XmlQualifiedName lookupQName = new
XmlQualifiedName(bodyReader.LocalName, bodyReader.NamespaceURI);
message = CreateMessageCopy(message,bodyReader);
if (dispatchDictionary.ContainsKey(lookupQName))
{
return dispatchDictionary[lookupQName];
}
else
{
return defaultOperationName;
}
}
使用 GetReaderAtBodyContents 或其他可存取訊息本文內容的方法來存取訊息本文時,會導致訊息標示為「已讀取」,這表示無法對該訊息進行進一步的處理。 因此,作業選取器會使用下列程式碼中顯示的方法,建立傳入訊息的複本。 由於讀取器的位置在檢查期間並未變更,新建立的訊息便可用它來參考也複製的訊息屬性和訊息標頭,因此會對原始訊息進行完整複製:
private Message CreateMessageCopy(Message message,
XmlDictionaryReader body)
{
Message copy = Message.CreateMessage(message.Version,message.Headers.Action,body);
copy.Headers.CopyHeaderFrom(message,0);
copy.Properties.CopyProperties(message.Properties);
return copy;
}
將作業選取器新增至服務
服務分派作業選取器是 Windows Communication Foundation (WCF) 發送器的延伸模組。 若要選取雙工合約之回呼通道上的方法,也會使用用戶端作業選取器,其運作相當類似於此處描述的分派作業選取器,不過並沒有明確涵蓋在本範例中。
和大多數服務模型延伸項目一樣,分派作業選取器也會使用行為新增至發送器。 「行為」是組態物件,可將一或多個延伸模組新增至分派執行階段 (或用戶端執行階段),或者變更其設定。
由於作業選取器具有合約範圍,在這裡可適當實作的行為則是 IContractBehavior。 由於會在 Attribute 衍生類別上實作介面 (如下列程式碼所示),可以透過宣告方式將行為新增至服務合約。 每次開啟 ServiceHost 以及建置分派執行階段時,所有找到的行為便會做為合約上的屬性、作業和服務實作或者服務組態中的項目,接著自動新增這些行為並要求它們做為延伸項目或修改預設組態。
為達到簡潔性,下列程式碼摘錄只會顯示方法 ApplyDispatchBehavior 的實作,而這個方法會影響此範例中發送器的組態變更。 不會顯示其他方法,因為這些方法會傳回呼叫者而不會進行任何運作。
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)]
class DispatchByBodyElementBehaviorAttribute : Attribute, IContractBehavior
{
// public void AddBindingParameters(...)
// public void ApplyClientBehavior(...)
// public void Validate(...)
首先,ApplyDispatchBehavior 實作會藉由反覆查看服務端點之 OperationDescription 的 ContractDescription 項目,而設定作業選取器的查閱字典。 接著,檢查每個作業描述以確定是否有 DispatchBodyElementAttribute
行為,在這個範例中也會定義 IOperationBehavior 實作。 當這個類別也是行為時,則為被動類別,而且不會主動對分派執行階段提供組態變更。 所有方法會傳回呼叫者,而不會採取任何動作。 只會有作業行為,因此新分派機制需要的中繼資料 (也就是選取其作業項目上之本文項目的限定名稱) 可以和相對的作業產生關聯。
如果找到這類行為,就會從 XML 限定名稱 (QName
屬性) 建立值組,並且將作業名稱 (Name
屬性) 新增至字典。
填入字典之後,就會以此資訊建構新的 DispatchByBodyElementOperationSelector
,並設定為分派執行階段的作業選取器:
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
{
Dictionary<XmlQualifiedName,string> dispatchDictionary =
new Dictionary<XmlQualifiedName,string>();
foreach( OperationDescription operationDescription in
contractDescription.Operations )
{
DispatchBodyElementAttribute dispatchBodyElement =
operationDescription.Behaviors.Find<DispatchBodyElementAttribute>();
if ( dispatchBodyElement != null )
{
dispatchDictionary.Add(dispatchBodyElement.QName,
operationDescription.Name);
}
}
dispatchRuntime.OperationSelector =
new DispatchByBodyElementOperationSelector(
dispatchDictionary,
dispatchRuntime.UnhandledDispatchOperation.Name);
}
}
實作服務
在此範例中實作的行為會直接影響解譯和分派網路訊息的方式,也就是服務合約的函式。 最後,應該在選擇要使用該行為的服務實作中,於服務合約層級宣告行為。
範例專案服務會將 DispatchByBodyElementBehaviorAttribute
合約行為套用至 IDispatchedByBody
服務合約,並以 DispatchBodyElementAttribute
作業行為標示 OperationForBodyA()
和 OperationForBodyB()
這兩個作業。 已開啟實作此合約之服務的服務主機時,發送器產生器 (Builder) 會如前所述挑選此中繼資料。
由於作業選取器只會根據訊息本文項目進行分派,而忽略 "Action",因此需要告知執行階段不要在傳回的回覆上檢查 "Action" 標頭,方法是將萬用字元 "*" 指派給 ReplyAction
的 OperationContractAttribute 屬性即可。 接著,需要讓預設作業的 "Action" 屬性設定為萬用字元 "*"。 預設作業會接收無法分派也沒有 DispatchBodyElementAttribute
的所有訊息:
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples"),
DispatchByBodyElementBehavior]
public interface IDispatchedByBody
{
[OperationContract(ReplyAction="*"),
DispatchBodyElement("bodyA","http://tempuri.org")]
Message OperationForBodyA(Message msg);
[OperationContract(ReplyAction = "*"),
DispatchBodyElement("bodyB", "http://tempuri.org")]
Message OperationForBodyB(Message msg);
[OperationContract(Action="*", ReplyAction="*")]
Message DefaultOperation(Message msg);
}
範例服務實作十分簡單。 每個方法都會將接收的訊息包裝至回覆訊息,並回應至用戶端。
執行建置範例
執行範例時,作業回應的本文內容會顯示在用戶端主控台視窗中,類似下列 (格式化) 輸出。
用戶端會分別將三個訊息傳送至其本文內容項目名稱為 bodyA
、bodyB
和 bodyX
的服務。 如前所述以及顯示的服務合約看來,具有 bodyA
項目的傳入訊息會分派至 OperationForBodyA()
方法。 由於具有 bodyX
本文項目的訊息並沒有明確的分派目標,便會將訊息分派至 DefaultOperation()
。 每個服務作業都會將接收的訊息本文包裝至方法特定的項目並傳回,這樣便可清楚對此範例相互關聯輸入和輸出訊息:
<?xml version="1.0" encoding="IBM437"?>
<replyBodyA xmlns="http://tempuri.org">
<q:bodyA xmlns:q="http://tempuri.org">test</q:bodyA>
</replyBodyA>
<?xml version="1.0" encoding="IBM437"?>
<replyBodyB xmlns="http://tempuri.org">
<q:bodyB xmlns:q="http://tempuri.org">test</q:bodyB>
</replyBodyB>
<?xml version="1.0" encoding="IBM437"?>
<replyDefault xmlns="http://tempuri.org">
<q:bodyX xmlns:q="http://tempuri.org">test</q:bodyX>
</replyDefault>
若要安裝、建置及執行範例
若要建置解決方案,請依照建置 Windows Communication Foundation 範例中的指示操作。
若要在單一或多部電腦組態中執行此範例,請遵循執行 Windows Communication Foundation 範例中的指示進行。