使用 WCF 通道模型调用 SAP 系统上的操作

使用 IRequestChannelIOutputChannel 通道来向适配器发送消息以调用 SAP 适配器的操作。 基本模式是使用绑定(SAPBinding)和从连接 URI 创建的终结点,为所需的通道形状创建通道工厂。 然后,创建一个 消息 实例,该实例表示符合目标作的消息架构的 SOAP 消息。 然后,可以使用从通道工厂创建的通道将此 消息 发送到 SAP 适配器。 如果使用 IRequestChannel,则会收到响应。 如果在 SAP 系统上执行作时遇到问题,SAP 适配器将引发 Microsoft.ServiceModel.Channels.Common.TargetSystemException

关于如何在 WCF 中使用 IRequestChannel 发送操作的概述,请参阅 客户端 Channel-Level 编程

本主题中的各节提供相关信息,帮助您使用 WCF 通道模型来调用 SAP 适配器上的操作。

支持 WCF 通道模型中的 BAPI 事务

使用同一 SAP 连接调用的所有 BAP 都属于 SAP 系统上的同一逻辑工作单元(LUW)或事务。 每个 WCF 通道表示与 SAP 系统的唯一连接。 若要使用 WCF 通道模型支持 BAPI 事务,请执行以下操作:

  • 确保 LUW(事务)中的每个 BAPI 都通过相同的通道发送。 这包括 BAPI_TRANSACTION COMMIT 或 BAPI_TRANSACTION_ROLLBACK操作。

  • 在调用通道上的下一个 BAPI 之前,请确保关掉任何接收到的 BAPI 响应消息。 (对于每一项操作你都应该这样做;对于 BAPIs 更是如此。)

    有关 BAPI 事务的更多信息,请参阅 SAP 中的 BAPIs 操作

将平面文件 IDOC 流式传输到 SAP 适配器

使用 SendIdoc 操作将平面文件(字符串)IDOC 发送到适配器。 IDOC 数据表示为在此操作中单个节点下的字符串。 因此,SAP 适配器支持请求消息上的节点值流式处理。 为了执行节点值流式处理,您必须使用能够流式传输 IDOC 数据的 System.ServiceModel.Channels.BodyWriter 来创建 SendIdoc 操作的请求消息。 有关如何执行此操作的信息,请参阅 使用 WCF 通道模型在 SAP 中流式传输 Flat-File IDOC 的指南

如何使用通道调用操作?

若要使用 IRequestChannel 调用作,请执行以下步骤。

如何使用一个 IRequestChannel 实例执行操作

  1. 生成通道工厂(ChannelFactory<IRequestChannel>)。 为此,必须指定绑定(SAPBinding)和终结点地址。 可以在代码中以强制方式指定绑定和终结点地址,也可以在配置中以声明方式指定终结点地址。 在打开工厂之前,您应为将要发送的操作设置所需的任何绑定属性。 有关如何在配置中指定绑定和终结点地址的详细信息,请参阅 使用 SAP 创建通道

    // Create a binding  
    SAPBinding binding = new SAPBinding();  
    // Create an endpoint address by using the connection URI  
    EndpointAddress endpointAddress = new EndpointAddress("sap://Client=800;lang=EN@A/YourSAPHost/00");  
    // Create the channel factory  
    ChannelFactory<IRequestChannel> factory = new ChannelFactory<IRequestChannel>(binding, address);  
    
  2. 使用 ClientCredentials 属性设置通道工厂的用户名密码凭据。

    factory.Credentials.UserName.UserName = "YourUserName";  
    factory.Credentials.UserName.Password = "YourPassword";  
    
  3. 打开通道工厂。

    factory.Open();  
    
  4. 从工厂取得一个通道并将其打开。

    IRequestChannel channel = factory.CreateChannel();  
    channel.Open();  
    
  5. 为目标操作创建 消息 实例。 请确保为目标操作指定了消息动作。 在此示例中,消息正文是通过在字符串上创建XmlReader来传递的。 目标操作在 SAP 系统上调用 SD_RFC_CUSTOMER_GET RFC 函数。

    string inputXml = "\<SD_RFC_CUSTOMER_GET xmlns="http://Microsoft.LobServices.Sap/2007/03/Rfc/\"> <KUNNR i:nil=\"true\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"> </KUNNR> <NAME1>AB*</NAME1> <CUSTOMER_T> </CUSTOMER_T> </SD_RFC_CUSTOMER_GET>";  
    
    //create an XML reader from the input XML  
    XmlReader reader = XmlReader.Create(new MemoryStream(Encoding.Default.GetBytes(inputXml)));  
    
    //create a WCF message from our XML reader  
    Message inputMessge = Message.CreateMessage(MessageVersion.Soap11, "http://Microsoft.LobServices.Sap/2007/03/Rfc/SD_RFC_CUSTOMER_GET", reader);  
    
  6. 调用通道上的 Request 方法,将消息发送到 SAP 适配器并接收答复。 如果 SAP 系统遇到异常,适配器将引发 TargetSystemException。 (对于非 SAP 异常,其他异常是可能的。可以从 TargetSystemException的 InnerException.Message 属性获取 SAP 错误的说明。

    try  
    {  
        Message messageOut = channel.Request(messageIn);  
    }  
    catch (Exception ex)  
    {  
        // handle exception  
    }  
    
  7. 处理响应。 在此示例中,对响应消息调用 GetReaderAtBodyContents 以获取消息正文。

    XmlReader readerOut = messageOut.GetReaderAtBodyContents();  
    
  8. 处理完响应消息后,请关闭读取器和消息。

    readerOut.Close();  
    messageOut.Close();  
    
  9. 请在使用完通道和通道工厂后关闭它们。 关闭工厂将关闭由该工厂创建的所有通道。

    channel.Close()  
    factory.Close();  
    
  10. 按照相同的步骤使用 IOutputChannel 形状发送消息,但以下各项除外:

  • 在步骤 1 中创建 ChannelFactory<IOutputChannel>

  • 在步骤 6 中调用通道上的 Send 方法。 channel.Send(messageIn);

  • 没有为 IOutputChannel 返回的响应消息。

示例:

以下示例演示如何使用 IRequestChannel 通道调用 RFC。 此示例调用 SD_RFC_CUSTOMER_GET RFC 以获取名称以“AB”开头的客户的列表。 响应消息通过 XmlReader 读取,并将返回的每个客户的编号和名称写入控制台。

using System;  
using System.Collections.Generic;  
using System.Text;  
  
using System.Xml;  
using System.IO;  
  
using System.ServiceModel;  
using Microsoft.Adapters.SAP;  
using Microsoft.ServiceModel.Channels;  
using System.ServiceModel.Channels;  
  
namespace SapRfcClientCM  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            //create a binding  
            SAPBinding binding = new SAPBinding();  
  
            //set up an endpoint address.  
            EndpointAddress endpointAddress = new EndpointAddress("sap://Client=800;lang=EN@A/YourSAPHost/00");  
  
            //create a channel factory, capable of sending a request to SAP and receiving a reply (IRequestChannel)  
            ChannelFactory<IRequestChannel> factory = new ChannelFactory<IRequestChannel>(binding, endpointAddress);  
  
            // add credentials  
            factory.Credentials.UserName.UserName = "YourUserName";  
            factory.Credentials.UserName.Password = "YourPassword";  
  
            //open the factory  
            factory.Open();  
  
            //obtain a channel from the factory by specifying the address you want to connect to  
            IRequestChannel channel = factory.CreateChannel();  
  
            //open the channel  
            channel.Open();  
  
            //create an XML message to send to the SAP system  
            //We are invoking the SD_RFC_CUSTOMER_GET RFC.  
            //The XML below specifies that we want to search for customers with names starting with "AB"  
            string inputXml = "<SD_RFC_CUSTOMER_GET xmlns=\"http://Microsoft.LobServices.Sap/2007/03/Rfc/\"> <KUNNR i:nil=\"true\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"> </KUNNR> <NAME1>AB*</NAME1> <CUSTOMER_T> </CUSTOMER_T> </SD_RFC_CUSTOMER_GET>";  
  
            //create an XML reader from the input XML  
            XmlReader readerOut = XmlReader.Create(new MemoryStream(Encoding.Default.GetBytes(inputXml)));  
  
            //create a WCF message from the XML reader  
            Message messageOut = Message.CreateMessage(MessageVersion.Default, "http://Microsoft.LobServices.Sap/2007/03/Rfc/SD_RFC_CUSTOMER_GET", readerOut);  
  
            //send the message to SAP and obtain a reply  
            Message messageIn = channel.Request(messageOut);  
  
            // Write the KUNNR and NAME1 fields for each returned record to the Console  
            Console.WriteLine("Results of SD_RFC_CUSTOMER_GET");  
            Console.WriteLine("KUNNR\t\tNAME1");  
  
            XmlReader readerIn = messageIn.GetReaderAtBodyContents();  
  
            while (readerIn.Read())  
            {  
                if (readerIn.IsStartElement())  
                {  
                    switch (readerIn.Name)  
                    {  
                        case "RFCCUST":  
                            Console.Write("\n");  
                            break;  
  
                        case "KUNNR":  
                            readerIn.Read();  
                            Console.Write(readerIn.ReadString() + "\t");  
                            break;  
  
                        case "NAME1":  
                            readerIn.Read();  
                            Console.Write(readerIn.ReadString() + "\t");  
                            break;  
  
                        default:  
                            break;  
                    }  
                }  
            }  
  
            // return the cursor  
            Console.WriteLine();  
  
            // Close the input reader  
            readerIn.Close();  
  
            // Close the input message. You should do this for every message you   
            // send on the channel  
            messageIn.Close();  
  
            // close the channel when you are done using it.  
            channel.Close();  
  
            //close the factory  
            //note: closing the factory will close all of its channels.  
            factory.Close();  
        }  
    }  
}  

另请参阅

使用 WCF 通道模型开发应用程序