다음을 통해 공유


WCF 채널 모델을 사용하여 SAP 시스템에서 인바운드 작업 수신

RFC 서버 역할을 하고 SAP 시스템에서 호출한 작업(예: IDOC 보내기 또는 RFC 호출)을 받으려면 System.ServiceModel.Channels.IReplyChannel 채널 셰이프를 통해 SAP 프로그램 ID에서 메시지를 수신 대기할 수 있는 채널 수신기를 만들어야 합니다.

채널 수신기(System.ServiceModel.Channels.IChannelListener)는 특정 WCF 엔드포인트에서 메시지를 받는 데 사용할 수 있는 WCF 통신 개체입니다. 채널 수신기는 클라이언트(SAP 시스템)에서 호출한 메시지를 서비스에서 수신할 수 있는 채널을 만들 수 있는 팩터리로 작동합니다. BuildChannelListener 메서드를 호출하여 Microsoft.Adapters.SAP.SAPBinding 개체에서 채널 수신기를 만듭니다. 이 메서드에 인바운드 작업을 수신할 SAP 프로그램 ID를 지정하는 SAP 연결 URI를 제공합니다.

SAP 어댑터는 IReplyChannel 채널 셰이프를 지원합니다. IReplyChannel 채널은 인바운드 요청-응답 메시지 교환 패턴을 지원합니다. 즉, 외부 프로그램이 채널을 통해 요청 메시지를 보내고 프로그램이 응답을 반환하는 패턴입니다.

WCF에서 IReplyChannel 을 사용하여 작업을 수신하는 방법에 대한 개요는 서비스 Channel-Level 프로그래밍을 참조하세요.

이 섹션에서는 SAP 시스템에서 작업을 수신하는 것과 관련된 다음 topics 설명합니다.

  • 채널 수신기를 사용하여 특정 작업을 필터링하는 방법입니다.

  • SAP 시스템에서 예외를 발생 하는 방법.

  • SAP 어댑터에서 인바운드 플랫 파일 IOC 스트리밍.

  • SAP 시스템에서 작업을 수신하는 방법입니다.

채널 수신기를 사용하여 작업을 필터링하려면 어떻게 하나요?

InboundActionCollection을 사용하여 작업 필터링

WCF LOB 어댑터 SDK는 채널 수신기에서 수신하고 애플리케이션 코드에 전달되는 작업을 필터링할 수 있도록 Microsoft.ServiceModel.Channels.InboundActionCollection 클래스를 제공합니다. 특정 작업을 필터링하려면 수신기 엔드포인트 URI를 사용하여 이 클래스의 instance 만듭니다. 그런 다음 각 대상 작업에 대한 (요청) 메시지 작업을 컬렉션에 추가합니다. 마지막으로 System.ServiceModel.Channels.BindingParameterCollection 개체에 인바운드 작업 컬렉션을 추가한 다음 이 바인딩 매개 변수 컬렉션을 호출에 전달하여 채널 수신기를 만듭니다.

SAP 시스템이 인바운드 작업 컬렉션에 없는 작업을 호출하는 경우:

  • SAP 어댑터는 "Rfc Server에서 들어오는 RFC 호출 [RFC_NAME]이 처리되지 않았습니다."라는 메시지와 함께 SAP 시스템의 호출자에게 EXCEPTION 예외를 반환합니다. 이 메시지에서 [RFC_NAME]은 RFC의 이름입니다(예: IDOC_INBOUND_ASYNCHRONOUS).

  • 어댑터는 받은 작업을 나타내는 메시지와 함께 Microsoft.ServiceModel.Channels.Common.AdapterException 을 throw합니다. 이 예외를 사용하는 방법에 대한 예제는 이 항목의 끝에 있는 예제를 참조하세요.

    다음 코드 예제에서는 InboundActionCollection 을 사용하여 단일 RFC Z_RFC_MKD_DIV 필터링하는 채널 수신기를 만드는 방법을 보여 줍니다.

// The connection Uri must specify listener parameters (or an R-type destination in saprfc.ini)  
// and credentials.  
Uri listeneraddress =  
    new Uri("sap://User=YourUserName;Passwd=YourPassword;Client=800;Lang=EN;@a/YourSAPHost/00?ListenerGwServ=SAPGATEWAY&ListenerGwHost=YourSAPHost&ListenerProgramId=SAPAdapter");  
  
// Create a binding and set AcceptCredentialsInUri to true  
SAPBinding binding = new SAPBinding();  
binding.AcceptCredentialsInUri = true;  
  
// Create an InboundActionCollection and add the message actions to listen for,  
// only the actions added to the InboundActionCollection are received on the channel.  
// In this case a single action is specified: http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_DIV  
InboundActionCollection actions = new InboundActionCollection(listeneraddress);  
actions.Add("http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_DIV");  
  
// Create a BindingParameterCollection and add the InboundActionCollection  
BindingParameterCollection bpcol = new BindingParameterCollection();  
bpcol.Add(actions);  
  
// Create the channel listener by specifying the binding parameter collection (to filter for the Z_RFC_MKD_DIV action)  
listener = binding.BuildChannelListener<IReplyChannel>(listeneraddress, bpcol);  

수동 필터링 작업

채널 수신기에 대한 인바운드 작업 컬렉션을 지정하지 않으면 SAP 시스템에서 호출하는 모든 작업이 코드에 전달됩니다. 인바운드 요청의 메시지 동작을 확인하여 이러한 작업을 수동으로 필터링할 수 있습니다.

콘텐츠에 따라 작업을 필터링하려는 시나리오도 있을 수 있습니다. 예를 들어 다음에서 IDOC를 수신하는 경우:

  • 문자열 형식( ReceiveIDocFormat 바인딩 속성은 String) 모든 IDOC는 ReceiveIdoc 작업을 사용하여 수신됩니다.

  • Rfc 형식( ReceiveIDocFormat 바인딩 속성은 Rfc) 모든 IDOC는 IDOC_INBOUND_ASYNCHRONOUS RFC 또는 INBOUND_IDOC_PROCESS RFC를 사용하여 수신됩니다.

    이 시나리오에서는 코드의 특정 IDOC 매개 변수(예: IDOC 형식)를 기반으로 필터링을 구현할 수 있습니다.

    작업을 수동으로 필터링할 때 처리하지 않는 작업에 대해 SAP 어댑터에 오류를 반환할 수 있습니다. 그러면 SAP 시스템의 호출자에게 EXCEPTION 예외가 발생합니다. SAP에서 예외를 발생시키고 싶지 않은 경우 빈 응답을 반환할 수도 있습니다.

    다음 코드에서는 Z_RFC_MKD_DIV 작업에 대해 수동으로 필터링하는 방법을 보여 있습니다.

// Get the message from the channel  
RequestContext rc = channel.ReceiveRequest();  
Message reqMessage = rc.RequestMessage;  
  
// Filter based on the message action.  
if (reqMessage.Headers.Action == "http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_DIV")  
{  
  
    // Process message and return response.  
    ...  
  
}  
else  
{  
    // If this isn't the correct message return an empty response or a fault message.  
    // This example returns an empty response.  
    rc.Reply(Message.CreateMessage(MessageVersion.Default, reqMessage.Headers.Action + "/response"));  
}  

SAP 시스템에서 예외를 발생하려면 어떻게 하나요?

SAP 시스템의 호출자에게 오류를 표시하려면 SOAP 오류가 있는 요청 메시지에 회신할 수 있습니다. SAP 어댑터에 SOAP 오류를 반환하면 어댑터는 SAP 시스템의 호출자에게 EXCEPTION 예외를 반환합니다. 예외 메시지는 SOAP 오류의 요소에서 만들어집니다.

SAP 어댑터는 다음 우선 순위에 따라 SAP EXCEPTION에 대한 메시지를 만듭니다.

  1. SOAP 오류에 세부 정보 개체가 포함된 경우 어댑터는 세부 정보를 문자열로 직렬화하고 예외 메시지가 이 문자열로 설정됩니다.

  2. SOAP 오류에 이유가 포함된 경우 예외 메시지는 해당 값으로 설정됩니다.

  3. 그렇지 않으면 어댑터가 MessageFault 개체 자체를 문자열로 직렬화하고 예외 메시지가 이 문자열로 설정됩니다.

참고

어댑터는 오류 메시지만 사용하여 SAP 시스템에서 발생한 예외에 반환된 예외 메시지를 만듭니다. 따라서 이러한 엔터티에 대해 설정한 값은 전적으로 사용자에게 달려 있습니다.

WCF는 SYSTEM.ServiceModel.Channels.MessageFault 클래스를 제공하여 SOAP 오류의 메모리 내 표현을 캡슐화합니다. 오버로드된 정적 MessageFault.CreateFault 메서드를 사용하여 새 SOAP 오류를 만든 다음 적절한 Message.CreateMessage 오버로드를 호출하여 오류 메시지를 만들 수 있습니다. 또한 WCF는 MessageFault 개체를 사용하지 않고 오류 메시지를 만드는 CreateMessage의 오버로드를 제공합니다.

System.ServiceModel.Channels.RequestContext.Reply 메서드를 사용하여 오류 메시지를 어댑터에 반환합니다. SAP 어댑터는 오류 메시지에 대한 메시지 동작을 무시하므로 메시지 작업을 모든 값으로 설정할 수 있습니다.

다음 예제에서는 SAP 어댑터에 오류 메시지를 반환하는 방법을 보여줍니다. 이 예제에서는 채널 수신기 및 채널을 만드는 단계를 생략합니다.

RequestContext rc = channel.ReceiveRequest();  
…  
// Start processing the inbound message  
…  
// If an error is encountered return a fault to the SAP system  
// This example uses CreateMessage overload to create a fault message.  
// The overload takes a SOAP version, fault code, reason, and message action  
// The SAP adapter ignores the message action for a fault so you can set it to any value you want.   
Message faultMessage = Message.CreateMessage(MessageVersion.Default, new FaultCode("SAP Example Fault"), "Testing SAP Faults", rc.RequestMessage.Headers.Action + "/fault");  
  
rc.Reply(faultMessage);  

SAP 어댑터에서 인바운드 Flat-File IDOC 스트리밍

인바운드 ReceiveIdoc 작업의 어댑터에서 플랫 파일(문자열)IDOC를 받습니다. IDOC 데이터는 이 작업의 단일 노드 아래에 문자열로 표시됩니다. 이러한 이유로 SAP 어댑터는 요청 메시지에서 노드 값 스트리밍을 지원합니다. 노드 값 스트리밍을 수행하려면System.Xml Message.WriteBodyContents 메서드를 호출하여 ReceiveIdoc 작업에 대한 요청 메시지를 사용해야 합니다 . IDOC 데이터를 스트리밍할 수 있는 XmlDictionaryWriter입니다. 이 작업을 수행하는 방법에 대한 자세한 내용은 WCF 채널 모델을 사용하여 SAP의 스트리밍 Flat-File IDOC를 참조하세요.

IReplyChannel을 사용하여 SAP 시스템에서 작업을 수신하려면 어떻게 하나요?

WCF 채널 모델을 사용하여 SAP 시스템에서 작업을 받으려면 다음 단계를 수행합니다.

IReplyChannel을 사용하여 SAP 시스템에서 작업을 수신하려면

  1. SAPBinding의 instance 만들고 수신하려는 작업에 필요한 바인딩 속성을 로 설정합니다. 최소한 AcceptCredentialsInUri 바인딩 속성을 true로 설정해야 합니다. tRFC 서버 역할을 하려면 TidDatabaseConnectionString 바인딩 속성을 설정해야 합니다. 바인딩 속성에 대한 자세한 내용은 MySAP Business Suite 바인딩 속성용 BizTalk 어댑터에 대해 읽어보세요.

    SAPBinding binding = new SAPBinding();  
    binding.AcceptCredentialsInUri = true;  
    
  2. BindingParameterCollection을 만들고 수신하려는 작업의 작업이 포함된 InboundActionCollection을 추가합니다. 어댑터는 다른 모든 작업에 대해 SAP 시스템에 예외를 반환합니다. 이 단계는 선택 사항입니다. 자세한 내용은 WCF 채널 모델을 사용하여 SAP 시스템에서 인바운드 작업 수신을 참조하세요.

    InboundActionCollection actions = new InboundActionCollection(listeneraddress);  
    actions.Add("http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_DIV");  
    BindingParameterCollection bpcol = new BindingParameterCollection();  
    bpcol.Add(actions);  
    
  3. SAPBinding에서 BuildChannelListener<IReplyChannel> 메서드를 호출하여 채널 수신기를 만들고 엽니다. SAP 연결 URI를 이 메서드의 매개 변수 중 하나로 지정합니다. 연결 URI에는 SAP 시스템의 RFC 대상에 대한 매개 변수가 포함되어야 합니다. SAP 연결 URI에 대한 자세한 내용은 SAP 시스템 연결 URI 만들기를 참조하세요. 3단계에서 BindingParameterCollection 을 만든 경우 채널 수신기를 만들 때도 이를 지정합니다.

    Uri listeneraddress =  
        new Uri("sap://User=YourUserName;Passwd=YourPassword;Client=800;Lang=EN;@a/YourSAPHost/00?ListenerGwServ=SAPGATEWAY&ListenerGwHost=YourSAPHost&ListenerProgramId=SAPAdapter");  
    IChannelListener<IReplyChannel> listener = binding.BuildChannelListener<IReplyChannel>(connectionUri, bpcol);  
    listener.Open();  
    
  4. 수신기에서 AcceptChannel 메서드를 호출하여 IReplyChannel 채널을 가져와서 엽니다.

    IReplyChannel channel = listener.AcceptChannel();  
    channel.Open();  
    
  5. 채널에서 ReceiveRequest 를 호출하여 어댑터에서 다음 작업에 대한 요청 메시지를 가져옵니다.

    RequestContext rc = channel.ReceiveRequest();  
    
  6. 어댑터에서 보낸 요청 메시지를 사용합니다. RequestContextRequestMessage 속성에서 요청 메시지를 가져옵니다. XmlReader 또는 XmlDictionaryWriter를 사용하여 메시지를 사용할 수 있습니다.

    XmlReader reader = (XmlReader)rc.RequestMessage.GetReaderAtBodyContents();  
    
  7. SAP 시스템에 응답 또는 오류를 반환하여 작업을 완료합니다.

    1. 메시지를 처리하고 응답 메시지를 어댑터에 반환하여 SAP 시스템에 응답을 반환합니다. 이 예제에서는 빈 메시지를 반환합니다.

      respMessage = Message.CreateMessage(MessageVersion.Default, rc.RequestMessage.Headers.Action + "/response");  
      rc.Reply(respMessage);  
      
    2. 어댑터에 오류 메시지를 반환하여 SAP 시스템에 예외를 반환합니다. 메시지 작업, 오류 코드 및 이유에 대해 모든 값을 사용할 수 있습니다.

      MessageFault fault = MessageFault.CreateFault(new FaultCode("ProcFault"), "Processing Error");  
      Message respMessage = Message.CreateMessage(MessageVersion.Default, fault, String.Empty);  
      rc.Reply(respMessage);  
      
  8. 메시지를 보낸 후 요청 컨텍스트를 닫습니다.

    rc.Close();  
    
  9. 요청 처리를 완료하면 채널을 닫습니다.

    channel.Close()  
    

    중요

    작업 처리를 완료한 후 채널을 닫아야 합니다. 채널을 닫지 못하면 코드의 동작에 영향을 줄 수 있습니다.

  10. SAP 시스템에서 작업 수신이 완료되면 수신기를 닫습니다.

    listener.Close()  
    

    중요

    사용이 완료되면 수신기를 명시적으로 닫아야 합니다. 그렇지 않으면 프로그램이 제대로 작동하지 않을 수 있습니다. 수신기를 닫으면 수신기를 사용하여 만든 채널이 닫히지 않습니다. 또한 수신기를 사용하여 만든 각 채널을 명시적으로 닫아야 합니다.

예제

다음 예제에서는 SAP 시스템에서 Z_RFC_MKD_DIV RFC를 받습니다. 이 RFC는 두 숫자를 나눕니다. 이 예제의 구현에서는 InboundActionCollection 을 사용하여 Z_RFC_MKD_DIV 작업을 필터링하고 메시지가 수신되면 다음을 수행합니다.

  • 0이 아닌 경우 분할 결과를 콘솔에 쓰고 SAP 시스템에 반환합니다.

  • divisor가 0이면 결과 예외 메시지를 콘솔에 쓰고 SAP 시스템에 오류를 반환합니다.

  • SAP 시스템에서 다른 작업을 보낸 경우 콘솔에 메시지를 씁니다. 이 경우 어댑터 자체는 SAP 시스템에 오류를 반환합니다.

using System;  
using System.Collections.Generic;  
using System.Text;  
  
using System.Runtime.Serialization;  
using System.Xml;  
using System.IO;  
  
// Add WCF, Adapter LOB SDK, and SAP Adapter namepaces  
using System.ServiceModel;  
using Microsoft.Adapters.SAP;  
using Microsoft.ServiceModel.Channels;  
  
// Add this namespace to use Channel Model   
using System.ServiceModel.Channels;  
  
// Include this namespace for Adapter LOB SDK and SAP exceptions  
using Microsoft.ServiceModel.Channels.Common;  
  
// This sample demonstrates using the adapter as an rfc server over a channel.  
// The sample implements an RFC, Z_RFC_MKD_DIV that divides two numbers and returns the result  
// 1)  A SAPBinding instance is created and configured (AcceptCredentialsInUri is set true)  
// 2)  A binding parameter collection is created with an InboundAction collection that specifies  
//     target RFC (Z_RFC_MKD_DIV) so that only messages with this action will be received by the  
//     listener (and channel).  
// 3)  An <IReplyChannel> listener is created from the binding and binding parameter collection  
// 4)  A channel is created and opened to receive a request  
// 6)  When Z_RFC_MKD_DIV is received the two parameters are divided and the parameters and result  
//     are written to the console, then the result is returned to the adapter by using a template  
//     message.  
// 7)  If a divide by 0 occurs the exception message is written to the console and a  
//     fault is returned to the SAP system  
// 8)  If any other operation is received an error message is written to the console and the adapter  
///    returns a fault to the SAP system  
// 9)  IMPORTANT you must close the channel and listener to deregister them from the SAP Program ID.  
namespace SapRfcServerCM  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            // Variables to hold the listener and channel  
            IChannelListener<IReplyChannel> listener = null;  
            IReplyChannel channel = null;  
  
            Console.WriteLine("Sample started");  
            Console.WriteLine("Initializing and creating channel listener -- please wait");  
            try  
            {  
  
                // The connection Uri must specify listener parameters (or an R-type destination in saprfc.ini)  
                // and also credentials.  
                Uri listeneraddress =  
                    new Uri("sap://User=YourUserName;Passwd=YourPassword;Client=800;Lang=EN;@a/YourSAPHost/00?ListenerGwServ=SAPGATEWAY&ListenerGwHost=YourSAPHost&ListenerProgramId=SAPAdapter");  
  
                // Create a binding -- set AcceptCredentialsInUri true  
                SAPBinding binding = new SAPBinding();  
                binding.AcceptCredentialsInUri = true;  
  
                // Create a binding parameter collection with a list of SOAP actions to listen on  
                // in this case: http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_DIV  
                // This ensures that only these actions are received on the channel.  
                InboundActionCollection actions = new InboundActionCollection(listeneraddress);  
                actions.Add("http://Microsoft.LobServices.Sap/2007/03/Rfc/Z_RFC_MKD_DIV");  
                BindingParameterCollection bpcol = new BindingParameterCollection();  
                bpcol.Add(actions);  
  
                // Pass the Uri and the binding parameter collection (to specify the Z_RFC_MKD_DIV action)  
                listener = binding.BuildChannelListener<IReplyChannel>(listeneraddress, bpcol);  
  
                Console.WriteLine("Opening listener");  
                // Open the listener  
                listener.Open();  
  
                // Get an IReplyChannel  
                channel = listener.AcceptChannel();  
  
                Console.WriteLine("Opening channel");  
                // Open the channel  
                channel.Open();  
  
                Console.WriteLine("\nReady to receive Z_RFC_MKD_DIV RFC");  
  
                try  
                {  
                    // Get the message from the channel  
                    RequestContext rc = channel.ReceiveRequest();  
  
                    // Get the request message sent by SAP  
                    Message reqMessage = rc.RequestMessage;  
  
                    // get the message body  
                    XmlReader reader = reqMessage.GetReaderAtBodyContents();  
  
                    reader.ReadStartElement("Z_RFC_MKD_DIV");  
                    reader.ReadElementString("DEST");  
                    int x_in = int.Parse(reader.ReadElementString("X"));  
                    int y_in = int.Parse(reader.ReadElementString("Y"));  
                    reader.ReadEndElement();  
  
                    Console.WriteLine("\nRfc Received");  
                    Console.WriteLine("X =\t\t" + x_in);  
                    Console.WriteLine("Y =\t\t" + y_in);  
  
                    Message messageOut = null;  
  
                    try   
                    {  
                        int result_out = x_in/y_in;  
  
                        Console.WriteLine("RESULT =\t" + result_out.ToString());  
  
                        string out_xml = "<Z_RFC_MKD_DIVResponse xmlns=\"http://Microsoft.LobServices.Sap/2007/03/Rfc/\"><RESULT>" + result_out + "</RESULT></Z_RFC_MKD_DIVResponse>";  
                        StringReader sr = new StringReader(out_xml);  
                        reader = XmlReader.Create(sr);  
  
                        // create a response message  
                        // be sure to specify the response action  
                        messageOut = Message.CreateMessage(MessageVersion.Default, reqMessage.Headers.Action + "/response", reader);  
  
                    }  
                    catch (DivideByZeroException ex)  
                    {  
                        Console.WriteLine();  
                        Console.WriteLine(ex.Message + " Returning fault to SAP");  
  
                        // Create a message that contains a fault  
                        // The fault code and message action can be any value  
  
                        messageOut = Message.CreateMessage(MessageVersion.Default, new FaultCode("Fault"), ex.Message, string.Empty);  
                    }  
  
                    // Send the reply  
                    rc.Reply(messageOut);  
  
                    // Close the request context  
                    rc.Close();  
  
                }  
                catch (AdapterException aex)  
                {  
                    // Will get here if the message received was not in the InboundActionCollection  
                    Console.WriteLine();  
                    Console.WriteLine(aex.Message);  
                }  
  
                // Wait for a key to exit  
                Console.WriteLine("\nHit <RETURN> to end");  
                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);  
                }  
            }  
            finally  
            {  
                // IMPORTANT: close the channel and listener to stop listening on the Program ID  
                if (channel != null)  
                {  
                    if (channel.State == CommunicationState.Opened)  
                        channel.Close();  
                    else  
                        channel.Abort();  
                }  
  
                if (listener != null)  
                {  
                    if (listener.State == CommunicationState.Opened)  
                        listener.Close();  
                    else  
                        listener.Abort();  
                }  
            }  
        }  
    }  
}  

참고 항목

WCF 채널 모델을 사용하여 애플리케이션 개발