Freigeben über


Empfangen eingehender Vorgänge vom SAP-System mithilfe des WCF-Kanalmodells

Um als RFC-Server zu fungieren und vom SAP-System aufgerufene Vorgänge zu empfangen (z. B. das Senden eines IDOC oder das Aufrufen eines RFC), müssen Sie einen Kanallistener erstellen, der nachrichten von einer SAP-Programm-ID über ein System.ServiceModel.Channels.IReplyChannel-Kanal-Shape lauschen kann.

Ein Kanallistener (System.ServiceModel.Channels.IChannelListener) ist ein WCF-Kommunikationsobjekt, das zum Empfangen von Nachrichten von bestimmten WCF-Endpunkten verwendet werden kann. Der Kanallistener fungiert als Factory, über die Sie Kanäle erstellen können, über die von einem Client (dem SAP-System) aufgerufene Nachrichten von Ihrem Dienst empfangen werden können. Sie erstellen einen Kanallistener aus einem Microsoft.Adapters.SAP.SAPBinding-Objekt , indem Sie die BuildChannelListener-Methode aufrufen. Sie geben einen SAP-Verbindungs-URI an, der die SAP-Programm-ID angibt, von der eingehende Vorgänge an diese Methode empfangen werden.

Der SAP-Adapter unterstützt das IReplyChannel-Kanal-Shape . IReplyChannel-Kanäle unterstützen ein Austauschmuster für eingehende Anforderungs-Antwort-Nachrichten. Das heißt, ein Muster, bei dem ein externes Programm eine Anforderungsnachricht über den Kanal sendet und Ihr Programm eine Antwort zurückgibt.

Eine Übersicht über das Empfangen von Vorgängen mit einem IReplyChannel in WCF finden Sie unter Dienst Channel-Level Programmierung.

In diesem Abschnitt werden die folgenden Themen behandelt, die spezifisch für den Empfang von Vorgängen von einem SAP-System sind:

  • Filtern nach bestimmten Vorgängen mithilfe des Kanallisteners

  • So lösen Sie eine Ausnahme im SAP-System aus.

  • Streaming eingehender Flatfile-IDOCs aus dem SAP-Adapter.

  • So erhalten Sie Vorgänge vom SAP-System.

Wie filtere ich Vorgänge mit dem Kanallistener?

Verwenden einer InboundActionCollection zum Filtern von Vorgängen

Das WCF LOB Adapter SDK stellt die Microsoft.ServiceModel.Channels.InboundActionCollection-Klasse bereit, damit Sie Vorgänge filtern können, die von einem Kanallistener empfangen und an Ihren Anwendungscode übergeben werden. Um nach bestimmten Vorgängen zu filtern, erstellen Sie mithilfe des Listenerendpunkt-URI eine instance dieser Klasse. Anschließend fügen Sie die (Anforderungs-)Nachrichtenaktion für jeden Zielvorgang zur Sammlung hinzu. Schließlich fügen Sie die Auflistung eingehender Aktionen einem System.ServiceModel.Channels.BindingParameterCollection-Objekt hinzu und übergeben diese Bindungsparameterauflistung dann an den Aufruf, um den Kanallistener zu erstellen.

Wenn das SAP-System einen Vorgang aufruft, der sich nicht in der Sammlung eingehender Aktionen befindet:

  • Der SAP-Adapter gibt eine EXCEPTION-Ausnahme für den Aufrufer im SAP-System mit der folgenden Meldung zurück: "Der eingehende RFC-Aufruf [RFC_NAME] auf dem RFC-Server wird nicht verarbeitet". In dieser Nachricht ist [RFC_NAME] der Name des RFC (z. B. IDOC_INBOUND_ASYNCHRONOUS).

  • Der Adapter löst eine Microsoft.ServiceModel.Channels.Common.AdapterException mit einer Meldung aus, die den empfangenen Vorgang angibt. Ein Beispiel für die Verwendung dieser Ausnahme finden Sie im Beispiel am Ende dieses Themas.

    Im folgenden Codebeispiel wird gezeigt, wie Sie eine InboundActionCollection verwenden, um einen Kanallistener zu erstellen, der nach einem einzelnen RFC filtert, 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);  

Manuelle Filtervorgänge

Wenn Sie keine Sammlung eingehender Aktionen für den Kanallistener angeben, werden alle vom SAP-System aufgerufenen Vorgänge an Ihren Code übergeben. Sie können solche Vorgänge manuell filtern, indem Sie die Nachrichtenaktion eingehender Anforderungen überprüfen.

Es kann auch Szenarien geben, in denen Sie einen Vorgang basierend auf seinem Inhalt filtern möchten. Wenn Sie z. B. IDOCs in erhalten:

  • Zeichenfolgenformat (die ReceiveIDocFormat-Bindungseigenschaft ist String); alle IDOCs werden über den ReceiveIdoc-Vorgang empfangen.

  • RFC-Format (die ReceiveIDocFormat-Bindungseigenschaft ist Rfc); alle IDOCs werden entweder mit dem IDOC_INBOUND_ASYNCHRONOUS RFC oder dem INBOUND_IDOC_PROCESS RFC empfangen.

    In diesem Szenario können Sie eine Filterung basierend auf bestimmten IDOC-Parametern (z. B. dem IDOC-Typ) in Ihrem Code implementieren.

    Wenn Sie Vorgänge manuell filtern, können Sie für Vorgänge, die Sie nicht verarbeiten, einen Fehler an den SAP-Adapter zurückgeben. Dadurch wird die AUSNAHME für den Aufrufer im SAP-System ausgelöst. Sie können auch eine leere Antwort zurückgeben, wenn Sie keine Ausnahme für SAP auslösen möchten.

    Der folgende Code zeigt, wie Sie manuell nach dem Z_RFC_MKD_DIV-Vorgang filtern.

// 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"));  
}  

Wie kann ich eine Ausnahme im SAP-System auslösen?

Um dem Aufrufer im SAP-System einen Fehler anzuzeigen, können Sie auf eine Anforderungsnachricht mit einem SOAP-Fehler antworten. Wenn Sie einen SOAP-Fehler an den SAP-Adapter zurückgeben, gibt der Adapter eine EXCEPTION-Ausnahme an den Aufrufer im SAP-System zurück. Die Ausnahmemeldung wird aus den Elementen des SOAP-Fehlers erstellt.

Der SAP-Adapter erstellt die Meldung für die SAP-AUSNAHME gemäß der folgenden Rangfolge:

  1. Wenn der SOAP-Fehler ein Detailobjekt enthält, serialisiert der Adapter das Detail in eine Zeichenfolge, und die Ausnahmemeldung wird auf diese Zeichenfolge festgelegt.

  2. Wenn der SOAP-Fehler einen Grund enthält, wird die Ausnahmemeldung auf ihren Wert festgelegt.

  3. Andernfalls serialisiert der Adapter das MessageFault-Objekt selbst in eine Zeichenfolge, und die Ausnahmemeldung wird auf diese Zeichenfolge festgelegt.

Hinweis

Der Adapter verwendet nur die Fehlermeldung, um die Ausnahmemeldung zu erstellen, die in der Ausnahme zurückgegeben wird, die auf dem SAP-System ausgelöst wird. Daher sind die Werte, die Sie für diese Entitäten festlegen, vollständig Von Ihnen selbst.

WCF stellt die System.ServiceModel.Channels.MessageFault-Klasse bereit, um eine Im-Arbeitsspeicher-Darstellung eines SOAP-Fehlers zu kapseln. Sie können eine der statischen, überladenen MessageFault.CreateFault-Methoden verwenden, um einen neuen SOAP-Fehler zu erstellen, aus dem Sie dann eine Fehlermeldung erstellen können, indem Sie die entsprechende Message.CreateMessage-Überladung aufrufen. WCF stellt auch Überladungen von CreateMessage bereit, die eine Fehlermeldung erstellen, ohne ein MessageFault-Objekt zu verwenden.

Sie verwenden die System.ServiceModel.Channels.RequestContext.Reply-Methode , um die Fehlermeldung an den Adapter zurückzugeben. Der SAP-Adapter ignoriert die Nachrichtenaktion für Fehlermeldungen, sodass Sie die Nachrichtenaktion auf einen beliebigen Wert festlegen können.

Das folgende Beispiel zeigt, wie eine Fehlermeldung an den SAP-Adapter zurückgegeben wird. In diesem Beispiel werden die Schritte zum Erstellen des Kanallisteners und des Kanals weggelassen.

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 eingehender Flat-File IDOCs vom SAP-Adapter

Sie erhalten Flatfile-IDOCs (Zeichenfolgen) vom Adapter im Eingehenden ReceiveIdoc-Vorgang. Die IDOC-Daten werden in diesem Vorgang als Zeichenfolge unter einem einzelnen Knoten dargestellt. Aus diesem Grund unterstützt der SAP-Adapter das Streaming von Knotenwerten für die Anforderungsnachricht. Zum Ausführen des Knotenwertstreamings müssen Sie die Anforderungsnachricht für den ReceiveIdoc-Vorgang nutzen, indem Sie die Message.WriteBodyContents-Methode mit einem System.Xml aufrufen. XmlDictionaryWriter , das in der Lage ist, die IDOC-Daten zu streamen. Informationen dazu finden Sie unter Streaming Flat-File IDOCs in SAP mithilfe des WCF-Kanalmodells.

Wie erhalte ich Vorgänge von einem SAP-System mit einem IReplyChannel?

Führen Sie die folgenden Schritte aus, um Vorgänge von einem SAP-System mithilfe des WCF-Kanalmodells zu empfangen.

So empfangen Sie Vorgänge vom SAP-System mithilfe eines IReplyChannel

  1. Erstellen Sie eine instance von SAPBinding, und legen Sie die Bindungseigenschaften fest, die für die Vorgänge erforderlich sind, die Sie empfangen möchten. Mindestens müssen Sie die AcceptCredentialsInUri-Bindungseigenschaft auf true festlegen. Um als tRFC-Server zu fungieren, müssen Sie die Bindungseigenschaft TidDatabaseConnectionString festlegen. Weitere Informationen zu Bindungseigenschaften finden Sie unter Informationen zum BizTalk-Adapter für mySAP Business Suite-Bindungseigenschaften.

    SAPBinding binding = new SAPBinding();  
    binding.AcceptCredentialsInUri = true;  
    
  2. Erstellen Sie eine BindingParameterCollection, und fügen Sie eine InboundActionCollection hinzu, die die Aktionen der Vorgänge enthält, die Sie empfangen möchten. Der Adapter gibt für alle anderen Vorgänge eine Ausnahme für das SAP-System zurück. Dieser Schritt ist optional. Weitere Informationen finden Sie unter Empfangen eingehender Vorgänge vom SAP-System mithilfe des WCF-Kanalmodells.

    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. Erstellen Sie einen Kanallistener, indem Sie die BuildChannelListener-IReplyChannel-Methode<> für sapBinding aufrufen, und öffnen Sie ihn. Sie geben den SAP-Verbindungs-URI als einen der Parameter für diese Methode an. Der Verbindungs-URI muss Parameter für ein RFC-Ziel im SAP-System enthalten. Weitere Informationen zum SAP-Verbindungs-URI finden Sie unter Erstellen des SAP-Systemverbindungs-URI. Wenn Sie in Schritt 3 eine BindingParameterCollection erstellt haben, geben Sie dies auch beim Erstellen des Kanallisteners an.

    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. Rufen Sie einen IReplyChannel-Kanal ab, indem Sie die AcceptChannel-Methode im Listener aufrufen und öffnen.

    IReplyChannel channel = listener.AcceptChannel();  
    channel.Open();  
    
  5. Rufen Sie ReceiveRequest im Kanal auf, um die Anforderungsnachricht für den nächsten Vorgang vom Adapter abzurufen.

    RequestContext rc = channel.ReceiveRequest();  
    
  6. Nutzen Sie die vom Adapter gesendete Anforderungsnachricht. Sie erhalten die Anforderungsnachricht von der RequestMessage-Eigenschaft des RequestContext.You get the request message from the RequestMessage property of the RequestContext. Sie können die Nachricht mit einem XmlReader oder einem XmlDictionaryWriter nutzen.

    XmlReader reader = (XmlReader)rc.RequestMessage.GetReaderAtBodyContents();  
    
  7. Schließen Sie den Vorgang ab, indem Sie eine Antwort oder einen Fehler an das SAP-System zurückgeben:

    1. Verarbeiten Sie die Nachricht, und geben Sie eine Antwort an das SAP-System zurück, indem Sie die Antwortnachricht an den Adapter zurückgeben. In diesem Beispiel wird eine leere Nachricht zurückgegeben.

      respMessage = Message.CreateMessage(MessageVersion.Default, rc.RequestMessage.Headers.Action + "/response");  
      rc.Reply(respMessage);  
      
    2. Geben Sie eine Ausnahme an das SAP-System zurück, indem Sie eine Fehlermeldung an den Adapter zurückgeben. Sie können einen beliebigen Wert für die Nachrichtenaktion, den Fehlercode und den Grund verwenden.

      MessageFault fault = MessageFault.CreateFault(new FaultCode("ProcFault"), "Processing Error");  
      Message respMessage = Message.CreateMessage(MessageVersion.Default, fault, String.Empty);  
      rc.Reply(respMessage);  
      
  8. Schließen Sie den Anforderungskontext, nachdem Sie die Nachricht gesendet haben.

    rc.Close();  
    
  9. Schließen Sie den Kanal, wenn Sie die Verarbeitung der Anforderung abgeschlossen haben.

    channel.Close()  
    

    Wichtig

    Sie müssen den Kanal schließen, nachdem Sie die Verarbeitung des Vorgangs abgeschlossen haben. Ein Fehler beim Schließen des Kanals kann sich auf das Verhalten Ihres Codes auswirken.

  10. Schließen Sie den Listener, wenn Sie den Empfang von Vorgängen vom SAP-System abgeschlossen haben.

    listener.Close()  
    

    Wichtig

    Sie müssen den Listener explizit schließen, wenn Sie ihn verwenden. Andernfalls verhält sich Ihr Programm möglicherweise nicht ordnungsgemäß. Das Schließen des Listeners führt nicht zum Schließen von Kanälen, die mit dem Listener erstellt wurden. Sie müssen auch jeden kanal explizit schließen, der mit dem Listener erstellt wurde.

Beispiel

Im folgenden Beispiel wird ein RFC Z_RFC_MKD_DIV aus dem SAP-System empfangen. Dieser RFC teilt zwei Zahlen. Die Implementierung in diesem Beispiel verwendet eine InboundActionCollection , um nach dem Z_RFC_MKD_DIV-Vorgang zu filtern, und führt folgendes aus, wenn eine Nachricht empfangen wird:

  • Wenn der Divisor ungleich 0 ist, schreibt er das Ergebnis der Aufteilung in die Konsole und gibt es an das SAP-System zurück.

  • Wenn der Divisor 0 ist, schreibt er die resultierende Ausnahmemeldung in die Konsole und gibt einen Fehler an das SAP-System zurück.

  • Wenn ein anderer Vorgang vom SAP-System gesendet wird, wird eine Nachricht an die Konsole geschrieben. In diesem Fall gibt der Adapter selbst einen Fehler an das SAP-System zurück.

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

Weitere Informationen

Entwickeln von Anwendungen mithilfe des WCF-Kanalmodells