.NETでSAPへ接続する(SAP .NET Connector)

QiitaでSAPをやっている人なんているの？という突込みもあるがそれは気にしないことにして、.NETを利用しSAPへ接続する方法を紹介する。

SAPとは[SAP社](http://www.sap.com/japan/index.html)が提供する業務パッケージで、この中には受注/請求、在庫といった業務に欠かせないデータが蓄積されている。これを外に出して活用したり、あるいは外部システムからSAPにデータを入れたい、という状況でこうした技術の出番となるわけです。

## .NET Connectorのインストール
まずはSAPから.NET用のConnectorをダウンロードする。32bit/64bitがあるので注意が必要。
[SAP CONNECTOR FOR MICROSOFT .NET](https://websmp110.sap-ag.de/~form/handler?_APP=00200682500000002672&_EVENT=DISPLAY&_SCENARIO=&_HIER_KEY=501100035870000018527&_HIER_KEY=601100035870000225415&)

なお、上記のサイトにアクセスするにはSAP Service Marketplaceのアカウントが必要(ただ、パッと見アカウントは普通に作れるようなかんじもする)。

それで、動作には「Microsoft Visual C++ 2010 再頒布可能パッケージ」が必要なのでこちらもダウンロード。これも32bit/64bitがあるので環境に合わせてインストール。

これで準備は完了だ。

## web.config/app.configの設定
こちらは、コネクタと共に提供されているサンプルの中に`sample_configuration_destinations.config`という設定ファイルがあるので、此方を参考に設定する。
修正が必要なのは`SAP.Middleware.Connector`セクションの`ClientSettings`の箇所。これを自分の環境に応じて変更する。設定は`saplogon.ini`の設定と同義なので、それに合わせて編集する。

## SAPへのアクセス
いよいよ本題、SAPへの接続を行う。
接続には、大きく分けて2つの方法がある。

* サイレントログオン  
通常のDBアクセスと同じように、ユーザーID/パスワードを設定ファイルに記載しておきユーザー入力無しにログインする。
この場合、`RfcDestinationManager.GetDestination(ログイン先)`のみでログイン可能。
* ダイアログログオン  
ユーザーにID、パスワードを入力してもらってログインする。
この場合は、`RfcCustomDestination`を利用して、そこにユーザーIDとパスワードをセットしログインする。

イメージとしては下記のような感じになる。

```vbnet
Public Function Login() As RfcDestination
    Dim connection As RfcDestination = Nothing
    Dim destConnection As RfcDestination = RfcDestinationManager.GetDestination(Destination)
    If IsSilent Then
        connection = destConnection
    Else
        Dim userLogin As RfcCustomDestination = destConnection.CreateCustomDestination
        userLogin.User = User
        userLogin.Password = Password
        connection = userLogin
    End If

    If connection IsNot Nothing Then
        connection.Ping() 'confirm connection
    End If

    Return connection

End Function
```

これでSAPへの接続は完了。

## リモートファンクション(RFC)を実行する
SAPには外部コール可能なFunction(RFC)があり、これらをこのコネクタで呼び出すことができる。
具体的には、テーブルTFDIRで更新モードが「R」であるものがこれに該当する(BAPIもこれに含まれる)ので、ほしい情報を提供してくれるファンクションがあるかどうかはこれで検索をかけて調べることができる。

さて、サンプルの中にもあるがまさにこの「RFCを検索するファンクション」があるためこれの呼び出しを見てみる。

```vbnet
'SAPへの接続を読込
Dim destination As RfcDestination = RfcDestinationManager.GetDestination(ABAP_APP_SERVER)

'RFCを検索するRFC_FUNCTION_SEARCHの実行パラメーターを設定する
Dim rfcFunction As IRfcFunction = destination.Repository.CreateFunction("RFC_FUNCTION_SEARCH")
rfcFunction.SetValue("FUNCNAME", "RFC_*")
rfcFunction.SetValue("LANGUAGE", "JA")

'RFCを実行
rfcFunction.Invoke(destination)

'返り値のテーブルを取得
Dim table As IRfcTable = rfcFunction.GetTable("FUNCTIONS")

'テーブル内の内容を取得
For i As Integer = 0 To rfcTable.Count - 1
    table.CurrentIndex = i '読込対象行を設定
    Console.WriteLine("{0} FUNCNAME={1} GROUPNAME={2} APPL={3} HOST={4} STEXT={5}", _
        i.ToString(), _
        table.GetString("funcname"), _
        table.GetString("groupname"), _
        table.GetString("appl"), _
        table.GetString("host"), _
        table.GetString("stext"))
Next
```

全体としては以下のような流れになる。

* `destination.Repository.CreateFunction`で、呼び出し対象のRFCの`IRfcFunction`を作成する
* `IRfcFunction`に対し、`SetValue`でパラメーターをセットする。
* Invokeで呼出
* `GetValue`,`GetTable`等で実行結果を取得

なお、単純に`SetValue`では値がセットができない構造を持つパラメーターもある。
この場合は、以下のように`GetStructure`を使用する

```vbnet
Dim impStruct As IRfcStructure = func.GetStructure("IMPORTSTRUCT")
'取得した構造に値をセット
impStruct.SetValue("RFCFLOAT", 12345.678900000001)
impStruct.SetValue("RFCINT4", 12345)
...
```

RFCである`STFC_STRUCTURE
`のパラメータ`IMPORTSTRUCT`は、下記のように構造`RFCTEST`で定義されるパラメータとなっている。

![func_structure.PNG](https://qiita-image-store.s3.amazonaws.com/0/25990/28045aed-b656-c183-233d-70fd5f98f4bf.png)

当然、構造`RFCTEST`は、複数の項目で構成される。

![structure_param.PNG](https://qiita-image-store.s3.amazonaws.com/0/25990/7e262f28-e10c-6c17-0881-fd6881799df6.png)

こうしたパラメータに値をセットする際は、まず`GetStructure`で構造体を取得し、その後`SetValue`で取得した構造にパラメータをセットするという流れになる。

なお、これは返り値の場合も同様で、返り値の値が構造で定義される場合は`GetStructure`で構造を取得し、`GetString`等のメソッドで値を取得、という流れになる。

後はトランザクション処理などもあるが、普通呼出元クライアントでそんな制御はしないので、上記を覚えておけばほぼ対応できると思う。

## おすすめRFC
呼出方法はわかったが、そもそもどれを呼び出せばほしい情報が取れるのかわからん、ということはよくある。
そのため、ここでは業務シーンで利用価値がありそうなRFCを紹介して行こうと思う。

...加筆中