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 システムからの操作の受信に固有の次のトピックについて説明します。
チャネル リスナーを使用して特定の操作をフィルター処理する方法。
SAP システムで例外を発生させる方法。
SAP アダプターからの受信フラット ファイル IDOC のストリーミング。
SAP システムから操作を受信する方法。
チャネル リスナーを使用して操作をフィルターする方法
InboundActionCollection を使用して操作をフィルター処理する
WCF LOB アダプター SDK には 、Microsoft.ServiceModel.Channels.InboundActionCollection クラスが用意されており、チャネル リスナーによって受信され、アプリケーション コードに渡される操作をフィルター処理できます。 特定の操作をフィルター処理するには、リスナー エンドポイント URI を使用して、このクラスのインスタンスを作成します。 次に、各ターゲット操作の (要求) メッセージ アクションをコレクションに追加します。 最後に、受信アクション コレクションを System.ServiceModel.Channels.BindingParameterCollection オブジェクトに追加し、このバインド パラメーター コレクションを呼び出しに渡してチャネル リスナーを作成します。
SAP システムが受信アクション コレクションにない操作を呼び出す場合:
SAP アダプターは、SAP システムの呼び出し元に例外例外を返します。"Rfc Server の受信 RFC 呼び出し [RFC_NAME] は処理されません。" というメッセージが表示されます。 このメッセージの [RFC_NAME] は RFC の名前です (例: IDOC_INBOUND_ASYNCHRONOUS)。
アダプターは、受信した操作を示すメッセージを含む Microsoft.ServiceModel.Channels.Common.AdapterException をスローします。 この例外の使用方法の例については、このトピックの最後にある例を参照してください。
次のコード例は、 InboundActionCollection を使用して、1 つの 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 システム上の呼び出し元に例外例外を返します。 例外メッセージは、SOAP エラーの要素から作成されます。
SAP アダプターは、次の優先順位に従って SAP EXCEPTION のメッセージを作成します。
SOAP エラーに詳細オブジェクトが含まれている場合、アダプターは詳細を文字列にシリアル化し、例外メッセージはこの文字列に設定されます。
SOAP エラーに理由が含まれている場合、例外メッセージはその値に設定されます。
それ以外の場合、アダプターは MessageFault オブジェクト自体を文字列にシリアル化し、例外メッセージはこの文字列に設定されます。
注
アダプターは、エラー メッセージのみを使用して、SAP システムで発生した例外で返される例外メッセージを作成します。したがって、これらのエンティティに設定する値は完全にユーザーの責任です。
WCF には、SOAP エラーのメモリ内表現をカプセル化するための System.ServiceModel.Channels.MessageFault クラスが用意されています。 静的でオーバーロードされた MessageFault.CreateFault メソッドのいずれかを使用して、適切な Message.CreateMessage オーバーロードを呼び出してエラー メッセージを作成できる新しい SOAP エラーを作成できます。 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 アダプターは、要求メッセージでのノード値ストリーミングをサポートします。 ノード値ストリーミングを実行するには、IDOC データをストリーミングできる System.Xml.XmlDictionaryWriter を使用して Message.WriteBodyContents メソッドを呼び出して、ReceiveIdoc 操作の要求メッセージを使用する必要があります。 これを行う方法の詳細については、「 WCF チャネル モデルを使用した SAP Flat-File IDOC のストリーミング」を参照してください。
IReplyChannel を使用して SAP システムから操作を受け取る方法
WCF チャネル モデルを使用して SAP システムから操作を受信するには、次の手順を実行します。
IReplyChannel を使用して SAP システムから操作を受信するには
SAPBinding のインスタンスを作成し、受信する操作に必要なバインド プロパティを設定します。 少なくとも AcceptCredentialsInUri バインド プロパティを true に設定する必要があります。 tRFC サーバーとして機能するには、 TidDatabaseConnectionString バインディング プロパティを設定する必要があります。 バインド プロパティの詳細については、「 BizTalk Adapter for mySAP Business Suite のバインド プロパティ」を参照してください。
SAPBinding binding = new SAPBinding(); binding.AcceptCredentialsInUri = true;
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);
SAPBinding で BuildChannelListener<IReplyChannel> メソッドを呼び出してチャネル リスナーを作成し、開きます。 このメソッドのパラメーターの 1 つとして 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();
リスナーで AcceptChannel メソッドを呼び出して IReplyChannel チャネルを取得し、それを開きます。
IReplyChannel channel = listener.AcceptChannel(); channel.Open();
チャネルで ReceiveRequest を呼び出して、アダプターから次の操作の要求メッセージを取得します。
RequestContext rc = channel.ReceiveRequest();
アダプターによって送信されたリクエストメッセージを消費します。 要求メッセージは、 RequestContext の RequestMessage プロパティから取得 します。 XmlReader または XmlDictionaryWriter を使用してメッセージを使用できます。
XmlReader reader = (XmlReader)rc.RequestMessage.GetReaderAtBodyContents();
SAP システムに応答または障害を返して、操作を完了します。
応答メッセージをアダプターに返して、メッセージを処理し、SAP システムに応答を返します。 次の使用例は、空のメッセージを返します。
respMessage = Message.CreateMessage(MessageVersion.Default, rc.RequestMessage.Headers.Action + "/response"); rc.Reply(respMessage);
エラー メッセージをアダプターに返して、SAP システムに例外を返します。 メッセージ アクション、エラー コード、および理由には任意の値を使用できます。
MessageFault fault = MessageFault.CreateFault(new FaultCode("ProcFault"), "Processing Error"); Message respMessage = Message.CreateMessage(MessageVersion.Default, fault, String.Empty); rc.Reply(respMessage);
メッセージを送信した後、要求コンテキストを閉じます。
rc.Close();
要求の処理が完了したら、チャネルを閉じます。
channel.Close()
Von Bedeutung
操作の処理が完了したら、チャネルを閉じる必要があります。 チャネルを閉じできないと、コードの動作に影響する可能性があります。
SAP システムからの操作の受信が完了したら、リスナーを閉じます。
listener.Close()
Von Bedeutung
リスナーの使用が完了したら、リスナーを明示的に閉じる必要があります。そうしないと、プログラムが正しく動作しない可能性があります。 リスナーを閉じても、リスナーを使用して作成されたチャネルは閉じません。 リスナーを使用して作成された各チャネルを明示的に閉じる必要もあります。
例
次の例では、SAP システムから RFC Z_RFC_MKD_DIVを受信します。 この RFC では、2 つの数値が除算されます。 この例の実装では 、InboundActionCollection を使用してZ_RFC_MKD_DIV操作をフィルター処理し、メッセージを受信すると次の処理を行います。
除数が 0 以外の場合は、除算の結果をコンソールに書き込み、SAP システムに返します。
除数が 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();
}
}
}
}
}