使用 WCF 通道模型在 SAP 系统上调用操作
通过使用 IRequestChannel 或 IOutputChannel 通道形状向适配器发送消息,调用 SAP 适配器上的操作。 基本模式是使用绑定 (SAPBinding) 以及从连接 URI 创建的终结点,为所需通道形状创建通道工厂。 然后,创建一个 Message 实例,该实例表示符合目标操作的消息架构的 SOAP 消息。 然后,可以使用从通道工厂创建的通道将此 消息 发送到 SAP 适配器。 如果使用的是 IRequestChannel,则会收到响应。 如果在 SAP 系统上执行操作时出现问题,SAP 适配器将引发 Microsoft.ServiceModel.Channels.Common.TargetSystemException。
有关如何在 WCF 中使用 IRequestChannel 发送操作的概述,请参阅 客户端 Channel-Level 编程。
本主题中的各节提供了有助于使用 WCF 通道模型在 SAP 适配器上调用操作的信息。
在 WCF 通道模型中支持 BAPI 事务
使用相同 SAP 连接调用的所有 BAPI 都是 SAP 系统上的同一逻辑工作单元 (LUW) 或事务的一部分。 每个 WCF 通道表示与 SAP 系统的唯一连接。 若要使用 WCF 通道模型支持 BAPI 事务,请执行以下操作:
确保 LUW (事务) 中的每个 BAPI 都通过同一通道发送。 这包括BAPI_TRANSACTION COMMIT 或BAPI_TRANSACTION_ROLLBACK操作。
在调用通道上的下一个 BAPI 之前,请确保关闭为 BAPI 收到的任何响应消息。 (应针对每个操作执行此操作;但它对于 BAPIs.) 尤其重要
有关 BAPI 事务的详细信息,请参阅 对 SAP 中的 BAPI 的操作。
将平面文件 IDOC 流式传输到 SAP 适配器
使用 SendIdoc 操作将 idOC) 字符串 (平面文件发送到适配器。 在此操作中,IDOC 数据表示为单个节点下的字符串。 因此,SAP 适配器支持对请求消息进行节点值流式处理。 若要执行节点值流式处理,必须使用能够流式传输 IDOC 数据的 System.ServiceModel.Channels.BodyWriter 为 SendIdoc 操作创建请求消息。 有关如何执行此操作的信息,请参阅 使用 WCF 通道模型在 SAP 中流式处理 Flat-File IDOC。
如何使用通道调用操作?
若要使用 IRequestChannel 调用操作,请执行以下步骤。
如何使用 IRequestChannel 实例调用操作
(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);
使用 ClientCredentials 属性设置通道工厂的用户名密码凭据。
factory.Credentials.UserName.UserName = "YourUserName"; factory.Credentials.UserName.Password = "YourPassword";
打开通道工厂。
factory.Open();
从工厂获取通道并将其打开。
IRequestChannel channel = factory.CreateChannel(); channel.Open();
为目标操作创建 Message 实例。 请确保已指定目标操作的消息操作。 在此示例中,通过创建基于字符串的 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);
在通道上调用 Request 方法,将消息发送到 SAP 适配器并接收回复。 如果 SAP 系统遇到异常,适配器将引发 TargetSystemException。 (非 SAP exceptions 也可能出现其他异常。) 可以从 TargetSystemException 的 InnerException.Message 属性获取 SAP 错误的说明。
try { Message messageOut = channel.Request(messageIn); } catch (Exception ex) { // handle exception }
处理响应。 在此示例中,对响应消息调用 GetReaderAtBodyContents 以获取消息正文。
XmlReader readerOut = messageOut.GetReaderAtBodyContents();
处理完响应消息后,关闭读取器和消息。
readerOut.Close(); messageOut.Close();
使用完通道和通道工厂后,请关闭它们。 关闭工厂将关闭使用工厂创建的所有通道。
channel.Close() factory.Close();
按照相同的步骤使用 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();
}
}
}