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

另请参阅

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