在 SAP 中使用 WCF 服务模型接收传入的 RFC 调用

mySAP Business Suite 的 Microsoft BizTalk 适配器可以充当 RFC 服务器来接收 SAP 系统调用的 RFC。

若要在 WCF 服务模型中接收入站 RFC,必须:

  • 确保 SAP 系统上存在 RFC 目标。

  • 确保 RFC 在 SAP 系统上定义。

  • 从适配器公开的元数据中生成 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 和 (input)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 服务协定(Rfc)的 WCF 服务。 此类的方法已经设置为存根。 此类在一个单独的文件中生成。 您可以直接在此类的方法中实现代码。

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);  
                }  
            }  
        }  
    }  
}  

另请参阅

使用 WCF 服务模型开发应用程序
RFC操作的消息模式