使用 WCF 服務模型在 SAP 中接收輸入 RFC 呼叫
Microsoft BizTalk Adapter for mySAP Business Suite 可作為 RFC 伺服器,以接收 SAP 系統所叫用的 RFC。
若要在 WCF 服務模型中接收輸入 RFC,您必須:
確定 SAP 系統上存在 RFC 目的地。
確定已定義 SAP 系統上的 RFC。
從配接器所公開的中繼資料產生 WCF 服務合約 (介面) 。 若要這樣做,請使用 [新增配接器服務參考][Visual Studio 外掛程式] 或 [ServiceModel 中繼資料公用程式工具] (svcutil.exe) 。
從這個介面實作 WCF 服務。 WCF 服務的方法包含處理 RFC 所需的邏輯,並傳回配接器 (的回應,因此 SAP 系統) 。
使用服務主機裝載此 WCF 服務, (System.ServiceModel.ServiceHost) 。
下列各節說明如何使用 SAP 配接器從 SAP 系統接收 RFC。
如何設定 SAP 系統以將 RFC 傳送至 SAP 配接器
在您可以將 RFC 從 SAP 系統傳送至 SAP 配接器之前,您必須先確定 SAP 系統上有下列內容:
SAP 配接器的 RFC 目的地必須存在。 SAP 配接器會向 RFC 目的地註冊自己,以接收來自 SAP 系統的 RFC。 您可以在 SAP 連線 URI 中提供參數,例如 SAP 閘道主機、SAP 閘道服務,以及配接器用來註冊本身的 SAP 程式識別碼。 如需如何在 SAP 上設定 RFC 目的地的詳細資訊,請參閱 建立 RFC、RFC 目的地,以及從 SAP 系統傳送 RFC。
RFC 必須在 SAP 系統上定義。 您必須建立函式模組,以定義 SAP 系統上的 RFC。 SAP 配接器會使用 SAP 系統上的 RFC 定義,在設計階段和執行時間擷取 RFC (的相關中繼資料) 。 如需詳細資訊,請參閱 在 SAP 系統中建立 RFC。
注意
您必須在 SAP 系統上定義 RFC;不過,您會在配接器用戶端程式代碼中實作 RFC。 RFC 必須在 SAP 系統上定義,讓配接器可以擷取 RFC 的中繼資料。
以下是 RFC 上 SAP 系統上原始程式碼的範例,其會新增兩個整數並傳回其結果。 程式碼只會透過指定的目的地呼叫 RFC。 函式的實作是由 SAP 配接器用戶端程式代碼所完成。
FUNCTION Z_RFC_SAMPLE_ADD.
*"---------------------------------------------------------------------*"*"Local interface:
*" IMPORTING
*" VALUE(X) TYPE INT4
*" VALUE(Y) TYPE INT4
*" VALUE(DEST) TYPE CHAR20 DEFAULT 'SAPADAPTER'
*" EXPORTING
*" VALUE(RESULT) TYPE INT4
*"---------------------------------------------------------------------CALL FUNCTION 'Z_RFC_MKD_ADD' DESTINATION DEST
EXPORTING X = X
Y = Y
IMPORTING RESULT = RESULT.
ENDFUNCTION.
RFC 的 WCF 服務合約
您可以使用新增配接器服務參考 Visual Studio 外掛程式或 ServiceModel 中繼資料公用程式工具 (svcutil.exe) ,為您想要從 SAP 系統接收的 RFC 產生 WCF 服務合約。 下列各節顯示針對Z_RFC_MKD_ADD作業產生的 Managed 程式碼類別和介面。
Rfc 介面 (WCF 服務合約)
SAP 配接器會在單一服務合約 「Rfc」 下呈現所有 RFC 作業。 這表示會針對您想要接收的所有 RFC 作業建立單一介面 Rfc。 每個目標 RFC 作業都會以這個介面的方法表示。 每個方法都會採用單一參數,代表作業要求訊息的訊息合約,並傳回 物件,該物件代表作業之回應訊息的訊息合約。
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://Microsoft.LobServices.Sap/2007/03/", ConfigurationName="Rfc")]
public interface Rfc {
// CODEGEN: Generating message contract since the wrapper namespace (http://Microsoft.LobServices.Sap/2007/03/Rfc/) of message Z_RFC_MKD_ADDRequest does not match the default value (http://Microsoft.LobServices.Sap/2007/03/)
[System.ServiceModel.OperationContractAttribute(Action="http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_ADD", ReplyAction="http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_ADD/response")]
Z_RFC_MKD_ADDResponse Z_RFC_MKD_ADD(Z_RFC_MKD_ADDRequest request);
}
要求和回應訊息
每個 RFC 作業都會採用代表要求訊息的參數,並傳回代表回應訊息的物件。 要求訊息的屬性包含 RFC 的 IMPORT 和 (輸入) CHANGING 參數。 回應訊息的屬性包含作業的 EXPORT 和 (輸出) CHANGING 參數。
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="Z_RFC_MKD_ADD", WrapperNamespace="http://Microsoft.LobServices.Sap/2007/03/Rfc/", IsWrapped=true)]
public partial class Z_RFC_MKD_ADDRequest {
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://Microsoft.LobServices.Sap/2007/03/Rfc/", Order=0)]
public string DEST;
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://Microsoft.LobServices.Sap/2007/03/Rfc/", Order=1)]
public System.Nullable<int> X;
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://Microsoft.LobServices.Sap/2007/03/Rfc/", Order=2)]
public System.Nullable<int> Y;
public Z_RFC_MKD_ADDRequest() {
}
public Z_RFC_MKD_ADDRequest(string DEST, System.Nullable<int> X, System.Nullable<int> Y) {
this.DEST = DEST;
this.X = X;
this.Y = Y;
}
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="Z_RFC_MKD_ADDResponse", WrapperNamespace="http://Microsoft.LobServices.Sap/2007/03/Rfc/", IsWrapped=true)]
public partial class Z_RFC_MKD_ADDResponse {
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://Microsoft.LobServices.Sap/2007/03/Rfc/", Order=0)]
public int RESULT;
public Z_RFC_MKD_ADDResponse() {
}
public Z_RFC_MKD_ADDResponse(int RESULT) {
this.RESULT = RESULT;
}
}
產生的 WCF 服務
新增配接器服務參考外掛程式也會產生 WCF 服務,以實作 WCF 服務合約 (Rfc) 。 這個類別的方法會經過 stubbed。 這個類別會在個別的檔案中產生。 您可以直接在此類別的方法中實作程式碼。
namespace SAPBindingNamespace {
public class SAPBindingService : Rfc {
// CODEGEN: Generating message contract since the wrapper namespace (http://Microsoft.LobServices.Sap/2007/03/Rfc/) of message Z_RFC_MKD_ADDRequest does not match the default value (http://Microsoft.LobServices.Sap/2007/03/)
public virtual Z_RFC_MKD_ADDResponse Z_RFC_MKD_ADD(Z_RFC_MKD_ADDRequest request) {
throw new System.NotImplementedException("The method or operation is not implemented.");
}
}
}
如何建立 RFC 伺服器應用程式
若要使用 WCF 服務模型從 SAP 系統接收 RFC,您可以遵循 使用 SAP 配接器概觀 WCF 服務模型概觀中的步驟。 當您新增服務端點時,請務必為服務合約指定 'Rfc', (建立和實作 WCF 服務) 的程式步驟 6。
下列程式碼示範如何使用 SAP 配接器從 SAP 系統接收Z_RFC_MKD_RFC的完整範例。 此 RFC 會採用兩個整數參數,並將結果傳回至 SAP 系統。
using System;
using System.Collections.Generic;
using System.Text;
// Add WCF, WCF LOB Adapter SDK, and SAP adapter namepaces
using System.ServiceModel;
using Microsoft.Adapters.SAP;
using Microsoft.ServiceModel.Channels;
// Include this namespace for the WCF LOB Adapter SDK and SAP adapter exceptions
using Microsoft.ServiceModel.Channels.Common;
namespace SapRfcServerSM
{
// Implement a WCF service callback class by sub-classing the generated service callback class (SAPBindingService).
// You must annotate this class with the InstanceContextMode.Single ServiceBehavior
// If you implement your code in SAPBindingService.cs be sure to annotate the SAPBindingService class
// The callback method should return a Z_RFC_MKD_ADDResponse to indicate successful processing
// or throw an exception to indicate an error.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,UseSynchronizationContext = false)]
class RfcServerClass : SAPBindingNamespace.SAPBindingService
{
public override Z_RFC_MKD_ADDResponse Z_RFC_MKD_ADD(Z_RFC_MKD_ADDRequest request)
{
// If either parameter is null, throw an exception
if (request.X == null || request.Y == null)
throw new System.ArgumentNullException();
int result = (int) (request.X + request.Y);
Console.WriteLine("\nRfc Received");
Console.WriteLine("X =\t\t" + request.X.ToString());
Console.WriteLine("Y =\t\t" + request.Y.ToString());
Console.WriteLine("Result =\t" + result);
Console.WriteLine("\nHit <RETURN> to end");
return new Z_RFC_MKD_ADDResponse(result);
}
}
class Program
{
static void Main(string[] args)
{
// Listener connection for the service URI -- the connection URI must contain credentials
Uri serviceUri = new Uri("sap://User=YourUserName;Passwd=YourPassword;Client=800;Lang=EN;@a/ADAPSAP47/00?ListenerGwServ=SAPGW00&ListenerGwHost=ADAPSAP47&ListenerProgramId=ADDER");
// The baseUri cannot contain userinfoparams or query_string parameters
Uri[] baseUri = new Uri[] { new Uri("sap://a/ADAPSAP47/00") };
Console.WriteLine("RFC server sample started");
// create service instance
RfcServerClass rfcServerInstance = new RfcServerClass();
try
{
Console.WriteLine("Initializing service host -- please wait");
// Create and initialize a service host
using (ServiceHost srvHost = new ServiceHost(rfcServerInstance, baseUri))
{
// Add service endpoint
// Specify AcceptCredentalsInUri=true for the binding
// NOTE: The contract for the service endpoint is "Rfc".
// This is the generated WCF service callback interface (see SAPBindingInterface.cs).
SAPBinding binding = new SAPBinding();
binding.AcceptCredentialsInUri = true;
srvHost.AddServiceEndpoint("Rfc", binding, serviceUri);
srvHost.Open();
Console.WriteLine("\nReady to receive Z_RFC_MKD_ADD RFC");
Console.WriteLine("Hit <RETURN> to end");
// Wait to receive request
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);
}
}
}
}
}