共用方式為


使用 WCF 通道模型從 SAP 系統接收輸入作業

若要作為 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 的訊息:

  1. 如果 SOAP 錯誤包含詳細資料物件,配接器會將詳細數據串行化為字串,而例外狀況訊息會設定為此字串。

  2. 如果 SOAP 錯誤包含原因,則例外狀況訊息會設定為其值。

  3. 否則,配接器會將 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 系統接收操作

  1. 建立 SAPBinding 的實體,並將您要接收之作業所需的系結屬性設定為 。 您至少必須將 AcceptCredentialsInUri 系結屬性設定為 true。 若要做為 tRFC 伺服器,您必須設定 TidDatabaseConnectionString 系結屬性。 如需系結屬性的詳細資訊,請參閱 閱讀 BizTalk Adapter for mySAP Business Suite 系結屬性

    SAPBinding binding = new SAPBinding();  
    binding.AcceptCredentialsInUri = true;  
    
  2. 建立 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);  
    
  3. 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();  
    
  4. 在接聽程式上叫用 AcceptChannel 方法並開啟它,以取得 IReplyChannel 通道。

    IReplyChannel channel = listener.AcceptChannel();  
    channel.Open();  
    
  5. 在通道上叫用 ReceiveRequest ,以從配接器取得下一個作業的要求訊息。

    RequestContext rc = channel.ReceiveRequest();  
    
  6. 處理轉接器所傳送的要求訊息。 您可以從 RequestContextRequestMessage 屬性取得要求訊息。 您可以使用 XmlReaderXmlDictionaryWriter 來取用訊息。

    XmlReader reader = (XmlReader)rc.RequestMessage.GetReaderAtBodyContents();  
    
  7. 將回應或錯誤傳回 SAP 系統,以完成作業:

    1. 處理訊息並透過返回回應訊息給配接器來向SAP系統回應。 此範例會傳回空的訊息。

      respMessage = Message.CreateMessage(MessageVersion.Default, rc.RequestMessage.Headers.Action + "/response");  
      rc.Reply(respMessage);  
      
    2. 將錯誤訊息傳回給配接器,以將例外狀況傳回SAP系統。 您可以針對訊息動作、錯誤碼和原因使用任何值。

      MessageFault fault = MessageFault.CreateFault(new FaultCode("ProcFault"), "Processing Error");  
      Message respMessage = Message.CreateMessage(MessageVersion.Default, fault, String.Empty);  
      rc.Reply(respMessage);  
      
  8. 傳送訊息後關閉請求上下文。

    rc.Close();  
    
  9. 當您完成處理要求時,請關閉通道。

    channel.Close()  
    

    這很重要

    完成處理作業之後,您必須關閉通道。 無法關閉通道可能會影響程式代碼的行為。

  10. 當您完成從 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();  
                }  
            }  
        }  
    }  
}  

另請參閱

使用 WCF 通道模型開發應用程式