Condividi tramite


Ricevere operazioni in ingresso dal sistema SAP usando il modello di canale WCF

Per fungere da server RFC e ricevere operazioni richiamate dal sistema SAP (ad esempio l'invio di un IDOC o la chiamata di un RFC), è necessario creare un listener del canale in grado di ascoltare i messaggi da un ID programma SAP su una forma di canale System.ServiceModel.Channels.IReplyChannel .

Un listener del canale (System.ServiceModel.Channels.IChannelListener) è un oggetto di comunicazione WCF che può essere usato per ricevere messaggi da endpoint WCF specifici. Il listener del canale funziona come factory da cui è possibile creare canali su cui i messaggi richiamati da un client (sistema SAP) possono essere ricevuti dal servizio. Si crea un listener del canale da un oggetto Microsoft.Adapters.SAP.SAPBinding richiamando il metodo BuildChannelListener . Specificare un URI di connessione SAP che specifica l'ID programma SAP da cui verranno ricevute le operazioni in ingresso a questo metodo.

L'adattatore SAP supporta la forma del canale IReplyChannel . I canali IReplyChannel supportano un modello di scambio di messaggi di richiesta-risposta in ingresso. Ovvero, un modello in cui un programma esterno invia un messaggio di richiesta sul canale e il programma restituisce una risposta.

Per una panoramica di come ricevere le operazioni usando un IReplyChannel in WCF, vedere Programmazione del servizio Channel-Level.

Questa sezione illustra gli argomenti seguenti specifici per la ricezione di operazioni da un sistema SAP:

  • Come filtrare per operazioni specifiche usando il listener del canale.

  • Come generare un'eccezione nel sistema SAP.

  • Streaming di IDOC di file flat in ingresso dalla scheda SAP.

  • Come ricevere operazioni dal sistema SAP.

Come si filtrano le operazioni usando il listener del canale?

Uso di un oggetto InboundActionCollection per filtrare le operazioni

WCF LOB Adapter SDK fornisce la classe Microsoft.ServiceModel.Channels.InboundActionCollection per consentire di filtrare le operazioni ricevute da un listener del canale e passate al codice dell'applicazione. Per filtrare le operazioni specifiche, creare un'istanza di questa classe usando l'URI dell'endpoint del listener. Si aggiunge quindi l'azione del messaggio (richiesta) per ogni operazione di destinazione alla raccolta. Infine, si aggiunge la raccolta di azioni in ingresso a un oggetto System.ServiceModel.Channels.BindingParameterCollection e quindi si passa questa raccolta di parametri di associazione nella chiamata per creare il listener del canale.

Se il sistema SAP richiama un'operazione non presente nella raccolta di azioni in ingresso:

  • L'adattatore SAP restituisce un'eccezione EXCEPTION al chiamante nel sistema SAP con il messaggio seguente: "La chiamata RFC in ingresso [RFC_NAME] nel server Rfc non è gestita". In questo messaggio [RFC_NAME] è il nome della RFC (ad esempio, IDOC_INBOUND_ASYNCHRONOUS).

  • L'adapter genera un'eccezione Microsoft.ServiceModel.Channels.Common.AdapterException con un messaggio che indica l'operazione ricevuta. Per un esempio di come usare questa eccezione, vedere l'esempio alla fine di questo argomento.

    Nell'esempio di codice seguente viene illustrato come usare un oggetto InboundActionCollection per creare un listener del canale che filtra un singolo 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);  

Operazioni di filtro manuale

Se non si specifica una raccolta di azioni in ingresso per il listener del canale, tutte le operazioni richiamate dal sistema SAP verranno passate al codice. È possibile filtrare manualmente tali operazioni controllando l'azione del messaggio delle richieste in ingresso.

Potrebbero essere presenti anche scenari in cui si vuole filtrare un'operazione in base al relativo contenuto. Ad esempio, se si riceve IDOCs in:

  • Formato stringa (la proprietà di associazione ReceiveIDocFormat è String); tutti i IDOC vengono ricevuti tramite l'operazione ReceiveIdoc.

  • Formato Rfc (la proprietà di associazione ReceiveIDocFormat è Rfc); tutti i IDOC vengono ricevuti usando il IDOC_INBOUND_ASYNCHRONOUS RFC o il INBOUND_IDOC_PROCESS RFC.

    In questo scenario può essere necessario implementare il filtro in base a parametri IDOC specifici (ad esempio il tipo IDOC) nel codice.

    Quando si filtrano manualmente le operazioni, è possibile restituire un errore all'adattatore SAP per le operazioni non gestite. Verrà generata l'eccezione EXCEPTION al chiamante nel sistema SAP. È anche possibile restituire una risposta vuota se non si vuole generare un'eccezione in SAP.

    Il codice seguente illustra come filtrare manualmente per l'operazione di 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"));  
}  

Come si genera un'eccezione nel sistema SAP?

Per indicare un errore al chiamante nel sistema SAP, è possibile rispondere a un messaggio di richiesta con un errore SOAP. Quando si restituisce un errore SOAP all'adattatore SAP, l'adattatore restituisce un'eccezione EXCEPTION al chiamante nel sistema SAP. Il messaggio di eccezione viene creato dagli elementi dell'errore SOAP.

L'adapter SAP crea il messaggio per SAP EXCEPTION in base all'ordine di precedenza seguente:

  1. Se l'errore SOAP contiene un oggetto dettaglio, l'adattatore serializza i dettagli in una stringa e il messaggio di eccezione viene impostato su questa stringa.

  2. Se l'errore SOAP contiene un motivo, il messaggio di eccezione viene impostato sul relativo valore.

  3. In caso contrario, l'adattatore serializza l'oggetto MessageFault stesso in una stringa e il messaggio di eccezione è impostato su questa stringa.

Nota

L'adapter usa solo il messaggio di errore per creare il messaggio di eccezione restituito nell'eccezione generata nel sistema SAP; pertanto, i valori impostati per queste entità sono completamente disponibili.

WCF fornisce la classe System.ServiceModel.Channels.MessageFault per incapsulare una rappresentazione in memoria di un errore SOAP. È possibile utilizzare uno dei metodi MessageFault.CreateFault statici di overload per creare un nuovo errore SOAP da cui è quindi possibile creare un messaggio di errore richiamando l'overload Message.CreateMessage appropriato. WCF fornisce anche overload di CreateMessage che creano un messaggio di errore senza utilizzare un oggetto MessageFault .

Utilizzare il metodo System.ServiceModel.Channels.RequestContext.Reply per restituire il messaggio di errore all'adapter. L'adapter SAP ignora l'azione del messaggio per i messaggi di errore, pertanto è possibile impostare l'azione del messaggio su qualsiasi valore.

Nell'esempio seguente viene illustrato come restituire un messaggio di errore all'adapter SAP. In questo esempio vengono omessi i passaggi per creare il listener e il canale del canale.

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

Streaming in ingresso Flat-File IDOC dall'adapter SAP

Si ricevono i IDOC (string) flat-file dall'adapter nell'operazione ReceiveIdoc in ingresso. I dati IDOC sono rappresentati come stringa in un singolo nodo in questa operazione. Per questo motivo, l'adattatore SAP supporta lo streaming node-value nel messaggio di richiesta. Per eseguire lo streaming node-value, è necessario utilizzare il messaggio di richiesta per l'operazione ReceiveIdoc richiamando il metodo Message.WriteBodyContents con un System.Xml. XmlDictionaryWriter in grado di trasmettere i dati IDOC. Per informazioni su come eseguire questa operazione, vedere Streaming Flat-File IDOCs in SAP usando il modello di canale WCF.

Come si ricevono operazioni da un sistema SAP usando un IReplyChannel?

Per ricevere operazioni da un sistema SAP usando il modello di canale WCF, seguire questa procedura.

Per ricevere operazioni dal sistema SAP usando un IReplyChannel

  1. Creare un'istanza di SAPBinding e impostare le proprietà di associazione necessarie per le operazioni che si desidera ricevere. È necessario impostare almeno la proprietà di associazione AcceptCredentialsInUri su true. Per fungere da server tRFC, è necessario impostare la proprietà di associazione TidDatabaseConnectionString . Per altre informazioni sulle proprietà di associazione, vedere Informazioni sull'adapter BizTalk per mySAP Business Suite Binding Properties.

    SAPBinding binding = new SAPBinding();  
    binding.AcceptCredentialsInUri = true;  
    
  2. Creare un oggetto BindingParameterCollection e aggiungere un oggetto InboundActionCollection contenente le azioni delle operazioni che si desidera ricevere. L'adattatore restituirà un'eccezione al sistema SAP per tutte le altre operazioni. Questo passaggio è facoltativo. Per altre informazioni, vedere Ricezione di operazioni in ingresso dal sistema SAP tramite il modello di canale WCF.

    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. Creare un listener del canale richiamando il metodo IReplyChannelListener<> in SAPBinding e aprirlo. Specificare l'URI di connessione SAP come uno dei parametri per questo metodo. L'URI di connessione deve contenere parametri per una destinazione RFC nel sistema SAP. Per altre informazioni sull'URI di connessione SAP, vedere Creare l'URI di connessione di sistema SAP. Se è stato creato un oggetto BindingParameterCollection nel passaggio 3, è necessario specificare questo valore anche quando si crea il listener del canale.

    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. Ottenere un canale IReplyChannel richiamando il metodo AcceptChannel nel listener e aprirlo.

    IReplyChannel channel = listener.AcceptChannel();  
    channel.Open();  
    
  5. Richiamare ReceiveRequest sul canale per ottenere il messaggio di richiesta per l'operazione successiva dall'adapter.

    RequestContext rc = channel.ReceiveRequest();  
    
  6. Utilizzare il messaggio di richiesta inviato dall'adapter. Viene visualizzato il messaggio di richiesta dalla proprietà RequestMessage di RequestContext. È possibile utilizzare il messaggio utilizzando xmlReader o XmlDictionaryWriter.

    XmlReader reader = (XmlReader)rc.RequestMessage.GetReaderAtBodyContents();  
    
  7. Completare l'operazione restituendo una risposta o un errore al sistema SAP:

    1. Elaborare il messaggio e restituire una risposta al sistema SAP restituendo il messaggio di risposta all'adapter. In questo esempio viene restituito un messaggio vuoto.

      respMessage = Message.CreateMessage(MessageVersion.Default, rc.RequestMessage.Headers.Action + "/response");  
      rc.Reply(respMessage);  
      
    2. Restituisce un'eccezione al sistema SAP restituendo un messaggio di errore all'adapter. È possibile usare qualsiasi valore per l'azione del messaggio, il codice di errore e il motivo.

      MessageFault fault = MessageFault.CreateFault(new FaultCode("ProcFault"), "Processing Error");  
      Message respMessage = Message.CreateMessage(MessageVersion.Default, fault, String.Empty);  
      rc.Reply(respMessage);  
      
  8. Chiudere il contesto della richiesta dopo l'invio del messaggio.

    rc.Close();  
    
  9. Chiudere il canale al termine dell'elaborazione della richiesta.

    channel.Close()  
    

    Importante

    Dopo aver completato l'elaborazione dell'operazione, è necessario chiudere il canale. La mancata chiusura del canale può influire sul comportamento del codice.

  10. Chiudere il listener al termine della ricezione delle operazioni dal sistema SAP.

    listener.Close()  
    

    Importante

    Al termine dell'uso, è necessario chiudere in modo esplicito il listener; in caso contrario, il programma potrebbe non comportarsi correttamente. La chiusura del listener non chiude i canali creati usando il listener. È anche necessario chiudere in modo esplicito ogni canale creato usando il listener.

Esempio

Nell'esempio seguente viene visualizzato un RFC, Z_RFC_MKD_DIV dal sistema SAP. Questo RFC divide due numeri. L'implementazione in questo esempio usa un oggetto InboundActionCollection per filtrare l'operazione di Z_RFC_MKD_DIV e esegue le operazioni seguenti quando viene ricevuto un messaggio:

  • Se il divisore non è zero, scrive il risultato della divisione nella console e lo restituisce al sistema SAP.

  • Se il divisore è zero, scrive il messaggio di eccezione risultante nella console e restituisce un errore al sistema SAP.

  • Se un'altra operazione viene inviata dal sistema SAP, scrive un messaggio nella console. In questo caso, l'adattatore stesso restituisce un errore al sistema 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();  
                }  
            }  
        }  
    }  
}  

Vedere anche

Sviluppare applicazioni tramite il modello di canale WCF