使用 WCF 通道模型從 SAP 系統接收輸入作業
若要作為 SAP 系統所叫用的 RFC 伺服器和接收作業 (例如傳送 IDOC 或叫用 RFC) ,您必須建立通道接聽程式,以透過 System.ServiceModel.Channels.IReplyChannel 通道圖形接聽 SAP 程式識別碼的訊息。
通道接聽程式 (System.ServiceModel.Channels.IChannelListener) 是 WCF 通訊物件,可用來接收來自特定 WCF 端點的訊息。 通道接聽程式會以處理站的形式運作,您可以從中建立用戶端所叫用訊息的通道, (SAP 系統) 可由您的服務接收。 您可以叫用BuildChannelListener方法,從Microsoft.Adapters.SAP.SAPBinding物件建立通道接聽程式。 您提供 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:
String 格式 (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 配接器時,配接器會將例外狀況傳回給 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 方法搭配System.Xml 來取用 ReceiveIdoc 作業的要求訊息 。能夠串流 IDOC 資料的 XmlDictionaryWriter 。 如需如何執行這項操作的詳細資訊,請參閱 使用 WCF 通道模型在 SAP 中串流 Flat-File IDOC。
如何使用 IReplyChannel 從 SAP 系統接收作業?
若要使用 WCF 通道模型從 SAP 系統接收作業,請執行下列步驟。
使用 IReplyChannel 從 SAP 系統接收作業
建立 SAPBinding 的實例,並設定您想要接收之作業所需的系結屬性。 您至少必須將 AcceptCredentialsInUri 系結屬性設定為 true。 若要做為 tRFC 伺服器,您必須設定 TidDatabaseConnectionString 系結屬性。 For more information about binding properties, see Read about BizTalk Adapter for mySAP Business Suite Binding Properties.
SAPBinding binding = new SAPBinding(); binding.AcceptCredentialsInUri = true;
建立 BindingParameterCollection 並新增 InputActionCollection ,其中包含您想要接收之作業的動作。 配接器會針對所有其他作業,將例外狀況傳回 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();
}
}
}
}
}