若要作為 RFC 伺服器和 SAP 系統叫用的接收作業(例如傳送 IDOC 或叫用 RFC),您必須建立通道接聽程式,以透過 System.ServiceModel.Channels.IReplyChannel 信道圖形接聽 SAP 程式識別符的訊息。
通道接聽程式 (System.ServiceModel.Channels.IChannelListener) 是 WCF 通訊物件,可用來接收來自特定 WCF 端點的訊息。 通道監聽器的作用如同一個工廠,您可以從中建立通道,讓由用戶端(SAP 系統)調用的訊息能夠被您的服務接收。 您可以從 Microsoft.Adapters.SAP.SAPBinding 物件建立通道接聽程式,方法是叫用 BuildChannelListener 方法。 您提供的 SAP 連線 URI 指定了 SAP 計劃識別碼,從該識別碼接收給此方法的輸入操作。
SAP 配接器支援 IReplyChannel 通道圖形。 IReplyChannel 通道支援輸入要求-回應消息交換模式。 也就是說,外部程式會透過通道傳送要求訊息,而您的程式會傳回回應的模式。
如需如何在 WCF 中使用 IReplyChannel 接收作業的概觀,請參閱 服務 Channel-Level 程式設計。
本節涵蓋從 SAP 系統接收作業的特定主題:
如何使用通道接聽程序篩選特定作業。
如何在 SAP 系統上引發例外狀況。
從 SAP 配接器串流輸入一般檔案 IDOC。
如何從 SAP 系統接收作業。
如何使用通道接聽器篩選操作?
使用 InboundActionCollection 篩選操作流程
WCF LOB 配接器 SDK 提供 Microsoft.ServiceModel.Channels.InboundActionCollection 類別,讓您篩選通道接聽程式所接收並傳遞至應用程式程式代碼的作業。 若要篩選特定作業,您可以使用接聽程式端點 URI 來建立此類別的實例。 接著,您會將每個目標作業的 [要求] 訊息動作新增至集合。 最後,您會將輸入動作集合新增至 System.ServiceModel.Channels.BindingParameterCollection 對象,然後將這個係結參數集合傳遞至呼叫以建立通道接聽程式。
如果 SAP 系統叫用不在輸入動作集合中的作業:
SAP 配接器會以下列訊息將例外狀況傳回給 SAP 系統上的呼叫端:「Rfc Server 上的傳入 RFC 呼叫 [RFC_NAME] 未處理」。 在此訊息中,[RFC_NAME] 是 RFC 的名稱(例如,IDOC_INBOUND_ASYNCHRONOUS)。
配接器會擲回 Microsoft.ServiceModel.Channels.Common.AdapterException ,並顯示已接收之作業的訊息。 如需如何使用這個例外狀況的範例,請參閱本主題結尾的範例。
下列程式代碼範例示範如何使用 InboundActionCollection 建立通道接聽程式,以篩選單一 RFC Z_RFC_MKD_DIV。
謹慎
此範例或指引會參考敏感性資訊,例如連接字串或使用者名稱和密碼。 請勿在程式代碼中硬式編碼這些值,並確定您使用最安全的驗證來保護機密數據。 如需詳細資訊,請參閱下列文件:
// The connection Uri must specify listener parameters (or an R-type destination in saprfc.ini) and credentials. Uri listeneraddress = new Uri("sap://User=YourUserName;Passwd=YourPassword;Client=800;Lang=EN;@a/YourSAPHost/00?ListenerGwServ=SAPGATEWAY&ListenerGwHost=YourSAPHost&ListenerProgramId=SAPAdapter"); // Create a binding and set AcceptCredentialsInUri to true SAPBinding binding = new SAPBinding(); binding.AcceptCredentialsInUri = true; // Create an InboundActionCollection and add the message actions to listen for, // only the actions added to the InboundActionCollection are received on the channel. // In this case a single action is specified: http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_DIV InboundActionCollection actions = new InboundActionCollection(listeneraddress); actions.Add("http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_DIV"); // Create a BindingParameterCollection and add the InboundActionCollection BindingParameterCollection bpcol = new BindingParameterCollection(); bpcol.Add(actions); // Create the channel listener by specifying the binding parameter collection (to filter for the Z_RFC_MKD_DIV action) listener = binding.BuildChannelListener<IReplyChannel>(listeneraddress, bpcol);
手動篩選作業
如果您未指定通道接聽程式的輸入動作集合,則 SAP 系統叫用的所有作業都會傳遞至您的程式代碼。 您可以檢查輸入要求的訊息動作,手動篩選這類作業。
您可能也想要根據作業內容來篩選作業的情況。 例如,如果您在下列專案中收到IDOC:
字串格式( ReceiveIDocFormat 系結屬性為 String):所有 IDOC 都會使用 ReceiveIdoc 作業接收。
Rfc 格式( ReceiveIDocFormat 系結屬性為 Rfc):所有 IDOC 都會使用 IDOC_INBOUND_ASYNCHRONOUS RFC 或 INBOUND_IDOC_PROCESS RFC 來接收。
在此案例中,您可能會想要根據程序代碼中的特定 IDOC 參數(例如 IDOC 類型)來實作篩選。
手動篩選作業時,您可以針對您未處理的作業,將錯誤傳回 SAP 配接器。 這會對 SAP 系統上的呼叫端引發 EXCEPTION 例外狀況。 如果您不想在 SAP 上引發例外狀況,您也可以傳回空的回應。
下列程式代碼示範如何手動篩選Z_RFC_MKD_DIV作業。
// Get the message from the channel
RequestContext rc = channel.ReceiveRequest();
Message reqMessage = rc.RequestMessage;
// Filter based on the message action.
if (reqMessage.Headers.Action == "http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_DIV")
{
// Process message and return response.
...
}
else
{
// If this isn't the correct message return an empty response or a fault message.
// This example returns an empty response.
rc.Reply(Message.CreateMessage(MessageVersion.Default, reqMessage.Headers.Action + "/response"));
}
如何在 SAP 系統上引發例外狀況?
若要向 SAP 系統上的呼叫端指出錯誤,您可以使用 SOAP 錯誤回覆要求訊息。 當您將SOAP錯誤傳回SAP配接器時,配接器會將EXCEPTION例外狀況傳回 SAP 系統上的呼叫端。 例外狀況訊息是從 SOAP 錯誤的元素建立。
SAP 配接器會根據下列優先順序來建立 SAP EXCEPTION 的訊息:
如果 SOAP 錯誤包含詳細資料物件,配接器會將詳細數據串行化為字串,而例外狀況訊息會設定為此字串。
如果 SOAP 錯誤包含原因,則例外狀況訊息會設定為其值。
否則,配接器會將 MessageFault 物件本身串行化為字串,而例外狀況訊息會設定為此字串。
備註
適配器僅使用錯誤訊息來建立在 SAP 系統上引發的例外中傳回的例外訊息;因此,您為這些實體設定的值完全由您決定。
WCF 提供 System.ServiceModel.Channels.MessageFault 類別,以封裝 SOAP 錯誤的記憶體內部表示法。 您可以使用任何靜態多載 的 MessageFault.CreateFault 方法來建立新的 SOAP 錯誤,然後叫用適當的 Message.CreateMessage 多載來建立錯誤訊息。 WCF 也提供 CreateMessage 的多載,可在不使用 MessageFault 對象的情況下建立錯誤訊息。
您可以使用 System.ServiceModel.Channels.RequestContext.Reply 方法,將錯誤訊息傳回配接器。 SAP 配接器會忽略錯誤訊息的訊息動作,因此您可以將訊息動作設定為任何值。
下列範例示範如何將錯誤訊息傳回 SAP 配接器。 此範例會省略建立通道接聽程式和通道的步驟。
RequestContext rc = channel.ReceiveRequest();
…
// Start processing the inbound message
…
// If an error is encountered return a fault to the SAP system
// This example uses CreateMessage overload to create a fault message.
// The overload takes a SOAP version, fault code, reason, and message action
// The SAP adapter ignores the message action for a fault so you can set it to any value you want.
Message faultMessage = Message.CreateMessage(MessageVersion.Default, new FaultCode("SAP Example Fault"), "Testing SAP Faults", rc.RequestMessage.Headers.Action + "/fault");
rc.Reply(faultMessage);
從 SAP 配接器串流輸入 Flat-File IDOC
您會在入站 ReceiveIdoc 作業中,從配接器接收平面檔案(字串格式)IDOC。 IDOC 數據在此作業中會以單一節點下的字串來表示。 基於這個理由,SAP 配接器支援要求訊息上的節點值串流。 若要執行節點值串流,您必須叫用 Message.WriteBodyContents 方法搭配能夠串流 IDOC 數據的 System.Xml.XmlDictionaryWriter ,以取用 ReceiveIdoc 作業的要求訊息。 如需了解如何執行這項作業的資訊,請參閱 使用 WCF 通道模型在 SAP 中串流 Flat-File IDOC。
如何使用 IReplyChannel 從 SAP 系統接收作業?
若要使用 WCF 通道模型從 SAP 系統接收作業,請執行下列步驟。
使用 IReplyChannel 從 SAP 系統接收操作
建立 SAPBinding 的實體,並將您要接收之作業所需的系結屬性設定為 。 您至少必須將 AcceptCredentialsInUri 系結屬性設定為 true。 若要做為 tRFC 伺服器,您必須設定 TidDatabaseConnectionString 系結屬性。 如需系結屬性的詳細資訊,請參閱 閱讀 BizTalk Adapter for mySAP Business Suite 系結屬性。
SAPBinding binding = new SAPBinding(); binding.AcceptCredentialsInUri = true;
建立 BindingParameterCollection 並新增 InboundActionCollection ,其中包含您要接收之作業的動作。 配接器會將所有其他作業的例外狀況傳回 SAP 系統。 這個步驟是選擇性的。 如需詳細資訊,請參閱 使用WCF通道模型從SAP系統接收輸入作業。
InboundActionCollection actions = new InboundActionCollection(listeneraddress); actions.Add("http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_DIV"); BindingParameterCollection bpcol = new BindingParameterCollection(); bpcol.Add(actions);
在 SAPBinding 上叫用 BuildChannelListener<IReplyChannel> 方法並加以開啟,以建立通道接聽程式。 您可以將 SAP 連線 URI 指定為此方法的其中一個參數。 連接 URI 必須包含 SAP 系統中 RFC 目標位址的參數。 如需 SAP 連線 URI 的詳細資訊,請參閱 建立 SAP 系統連線 URI。 如果您在步驟 3 中創建了BindingParameterCollection,也請於創建通道監聽器時指定它。
謹慎
此範例或指引會參考敏感性資訊,例如連接字串或使用者名稱和密碼。 請勿在程式代碼中硬式編碼這些值,並確定您使用最安全的驗證來保護機密數據。 如需詳細資訊,請參閱下列文件:
Uri listeneraddress = new Uri("sap://User=YourUserName;Passwd=YourPassword;Client=800;Lang=EN;@a/YourSAPHost/00?ListenerGwServ=SAPGATEWAY&ListenerGwHost=YourSAPHost&ListenerProgramId=SAPAdapter"); IChannelListener<IReplyChannel> listener = binding.BuildChannelListener<IReplyChannel>(connectionUri, bpcol); listener.Open();
在接聽程式上叫用 AcceptChannel 方法並開啟它,以取得 IReplyChannel 通道。
IReplyChannel channel = listener.AcceptChannel(); channel.Open();
在通道上叫用 ReceiveRequest ,以從配接器取得下一個作業的要求訊息。
RequestContext rc = channel.ReceiveRequest();
處理轉接器所傳送的要求訊息。 您可以從 RequestContext 的 RequestMessage 屬性取得要求訊息。 您可以使用 XmlReader 或 XmlDictionaryWriter 來取用訊息。
XmlReader reader = (XmlReader)rc.RequestMessage.GetReaderAtBodyContents();
將回應或錯誤傳回 SAP 系統,以完成作業:
處理訊息並透過返回回應訊息給配接器來向SAP系統回應。 此範例會傳回空的訊息。
respMessage = Message.CreateMessage(MessageVersion.Default, rc.RequestMessage.Headers.Action + "/response"); rc.Reply(respMessage);
將錯誤訊息傳回給配接器,以將例外狀況傳回SAP系統。 您可以針對訊息動作、錯誤碼和原因使用任何值。
MessageFault fault = MessageFault.CreateFault(new FaultCode("ProcFault"), "Processing Error"); Message respMessage = Message.CreateMessage(MessageVersion.Default, fault, String.Empty); rc.Reply(respMessage);
傳送訊息後關閉請求上下文。
rc.Close();
當您完成處理要求時,請關閉通道。
channel.Close()
這很重要
完成處理作業之後,您必須關閉通道。 無法關閉通道可能會影響程式代碼的行為。
當您完成從 SAP 系統接收作業時,請關閉接聽程式。
listener.Close()
這很重要
當您完成使用接聽程式時,您必須明確地關閉接聽程式;否則,您的程式可能無法正常運作。 關閉接聽程式不會關閉使用接聽程式建立的通道。 您也必須明確地關閉使用接聽程式建立的每個通道。
範例
下列範例會從 SAP 系統接收 RFC Z_RFC_MKD_DIV。 此 RFC 會分割兩個數位。 此範例中的實作會使用 InboundActionCollection 來篩選Z_RFC_MKD_DIV作業,並在收到訊息時執行下列動作:
如果除數不是零,它會將除法的結果寫入主控台,並將它傳回 SAP 系統。
如果除數為零,它會將產生的例外狀況訊息寫入主控台,並將錯誤傳回 SAP 系統。
如果 SAP 系統傳送任何其他作業,它會將訊息寫入主控台。 在此情況下,配接器本身會將錯誤傳回 SAP 系統。
謹慎
此範例或指引會參考敏感性資訊,例如連接字串或使用者名稱和密碼。 請勿在程式代碼中硬式編碼這些值,並確定您使用最安全的驗證來保護機密數據。 如需詳細資訊,請參閱下列文件:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.Xml;
using System.IO;
// Add WCF, Adapter LOB SDK, and SAP Adapter namepaces
using System.ServiceModel;
using Microsoft.Adapters.SAP;
using Microsoft.ServiceModel.Channels;
// Add this namespace to use Channel Model
using System.ServiceModel.Channels;
// Include this namespace for Adapter LOB SDK and SAP exceptions
using Microsoft.ServiceModel.Channels.Common;
// This sample demonstrates using the adapter as an rfc server over a channel.
// The sample implements an RFC, Z_RFC_MKD_DIV that divides two numbers and returns the result
// 1) A SAPBinding instance is created and configured (AcceptCredentialsInUri is set true)
// 2) A binding parameter collection is created with an InboundAction collection that specifies
// target RFC (Z_RFC_MKD_DIV) so that only messages with this action will be received by the
// listener (and channel).
// 3) An <IReplyChannel> listener is created from the binding and binding parameter collection
// 4) A channel is created and opened to receive a request
// 6) When Z_RFC_MKD_DIV is received the two parameters are divided and the parameters and result
// are written to the console, then the result is returned to the adapter by using a template
// message.
// 7) If a divide by 0 occurs the exception message is written to the console and a
// fault is returned to the SAP system
// 8) If any other operation is received an error message is written to the console and the adapter
/// returns a fault to the SAP system
// 9) IMPORTANT you must close the channel and listener to deregister them from the SAP Program ID.
namespace SapRfcServerCM
{
class Program
{
static void Main(string[] args)
{
// Variables to hold the listener and channel
IChannelListener<IReplyChannel> listener = null;
IReplyChannel channel = null;
Console.WriteLine("Sample started");
Console.WriteLine("Initializing and creating channel listener -- please wait");
try
{
// The connection Uri must specify listener parameters (or an R-type destination in saprfc.ini)
// and also credentials.
Uri listeneraddress =
new Uri("sap://User=YourUserName;Passwd=YourPassword;Client=800;Lang=EN;@a/YourSAPHost/00?ListenerGwServ=SAPGATEWAY&ListenerGwHost=YourSAPHost&ListenerProgramId=SAPAdapter");
// Create a binding -- set AcceptCredentialsInUri true
SAPBinding binding = new SAPBinding();
binding.AcceptCredentialsInUri = true;
// Create a binding parameter collection with a list of SOAP actions to listen on
// in this case: http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_DIV
// This ensures that only these actions are received on the channel.
InboundActionCollection actions = new InboundActionCollection(listeneraddress);
actions.Add("http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_DIV");
BindingParameterCollection bpcol = new BindingParameterCollection();
bpcol.Add(actions);
// Pass the Uri and the binding parameter collection (to specify the Z_RFC_MKD_DIV action)
listener = binding.BuildChannelListener<IReplyChannel>(listeneraddress, bpcol);
Console.WriteLine("Opening listener");
// Open the listener
listener.Open();
// Get an IReplyChannel
channel = listener.AcceptChannel();
Console.WriteLine("Opening channel");
// Open the channel
channel.Open();
Console.WriteLine("\nReady to receive Z_RFC_MKD_DIV RFC");
try
{
// Get the message from the channel
RequestContext rc = channel.ReceiveRequest();
// Get the request message sent by SAP
Message reqMessage = rc.RequestMessage;
// get the message body
XmlReader reader = reqMessage.GetReaderAtBodyContents();
reader.ReadStartElement("Z_RFC_MKD_DIV");
reader.ReadElementString("DEST");
int x_in = int.Parse(reader.ReadElementString("X"));
int y_in = int.Parse(reader.ReadElementString("Y"));
reader.ReadEndElement();
Console.WriteLine("\nRfc Received");
Console.WriteLine("X =\t\t" + x_in);
Console.WriteLine("Y =\t\t" + y_in);
Message messageOut = null;
try
{
int result_out = x_in/y_in;
Console.WriteLine("RESULT =\t" + result_out.ToString());
string out_xml = "<Z_RFC_MKD_DIVResponse xmlns=\"http://Microsoft.LobServices.Sap/2007/03/Rfc/\"><RESULT>" + result_out + "</RESULT></Z_RFC_MKD_DIVResponse>";
StringReader sr = new StringReader(out_xml);
reader = XmlReader.Create(sr);
// create a response message
// be sure to specify the response action
messageOut = Message.CreateMessage(MessageVersion.Default, reqMessage.Headers.Action + "/response", reader);
}
catch (DivideByZeroException ex)
{
Console.WriteLine();
Console.WriteLine(ex.Message + " Returning fault to SAP");
// Create a message that contains a fault
// The fault code and message action can be any value
messageOut = Message.CreateMessage(MessageVersion.Default, new FaultCode("Fault"), ex.Message, string.Empty);
}
// Send the reply
rc.Reply(messageOut);
// Close the request context
rc.Close();
}
catch (AdapterException aex)
{
// Will get here if the message received was not in the InboundActionCollection
Console.WriteLine();
Console.WriteLine(aex.Message);
}
// Wait for a key to exit
Console.WriteLine("\nHit <RETURN> to end");
Console.ReadLine();
}
catch (ConnectionException cex)
{
Console.WriteLine("Exception occurred connecting to the SAP system");
Console.WriteLine(cex.InnerException.Message);
}
catch (TargetSystemException tex)
{
Console.WriteLine("Exception occurred on the SAP system");
Console.WriteLine(tex.InnerException.Message);
}
catch (Exception ex)
{
Console.WriteLine("Exception is: " + ex.Message);
if (ex.InnerException != null)
{
Console.WriteLine("Inner Exception is: " + ex.InnerException.Message);
}
}
finally
{
// IMPORTANT: close the channel and listener to stop listening on the Program ID
if (channel != null)
{
if (channel.State == CommunicationState.Opened)
channel.Close();
else
channel.Abort();
}
if (listener != null)
{
if (listener.State == CommunicationState.Opened)
listener.Close();
else
listener.Abort();
}
}
}
}
}