使用 WCF 服务模型在 SAP 中接收入站 RFC 调用
适用于 mySAP Business Suite 的 Microsoft BizTalk 适配器可以充当 RFC 服务器来接收 SAP 系统调用的 RFC。
若要在 WCF 服务模型中接收入站 RFC,必须:
确保 SAP 系统上存在 RFC 目标。
确保在 SAP 系统上定义 RFC。
从适配器公开的元数据 (RFC 操作的接口) 生成 WCF 服务协定。 为此,请使用添加适配器服务参考 Visual Studio 插件或 ServiceModel 元数据实用工具 (svcutil.exe) 。
从此接口实现 WCF 服务。 WCF 服务的方法包含处理 RFC 并返回对适配器 (响应所需的逻辑,因此 SAP 系统) 。
使用服务主机 (System.ServiceModel.ServiceHost) 托管此 WCF 服务。
以下部分介绍如何使用 SAP 适配器从 SAP 系统接收 RFC。
如何设置 SAP 系统以将 RFC 发送到 SAP 适配器
在将 RFC 从 SAP 系统发送到 SAP 适配器之前,必须确保 SAP 系统上满足以下条件:
SAP 适配器的 RFC 目标必须存在。 SAP 适配器将自身注册到 RFC 目标,以接收来自 SAP 系统的 RFC。 在 SAP 连接 URI 中提供参数,例如 SAP 网关主机、SAP 网关服务和适配器用于注册自身的 SAP 程序 ID。 有关如何在 SAP 上设置 RFC 目标的信息,请参阅 创建 RFC、RFC 目标并从 SAP 系统发送 RFC。
必须在 SAP 系统上定义 RFC。 必须创建一个函数模块来定义 SAP 系统上的 RFC。 SAP 适配器使用 SAP 系统上的 RFC 定义在设计时和运行时) 检索有关 RFC (的元数据。 有关详细信息 ,请参阅在 SAP 系统中创建 RFC。
注意
必须在 SAP 系统上定义 RFC;但是,可以在适配器客户端代码中实现 RFC。 必须在 SAP 系统上定义 RFC,以便适配器可以检索 RFC 的元数据。
下面是 SAP 系统上 RFC 的源代码示例,该代码将添加两个整数并返回其结果。 代码仅通过指定目标调用 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操作生成的托管代码类和接口。
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) 。 此类的方法被插在一起。 此类在单独的文件中生成。 可以直接在此类的 方法中实现代码。
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 服务模型概述中的步骤操作。 在创建和实现 WCF 服务) 的过程 (步骤 6 添加服务终结点时,请务必为服务协定指定“Rfc”。
以下代码演示了如何使用 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);
}
}
}
}
}