Salesforce と kintone 間で顧客データを同期する方法

システムといえば昔は、膨大な時間をかけて複雑なソースコードを組み上げてようやく出来上がるもの という印象がありました。
近年では様々なものがクラウド化されていますが、現在ではクラウド上で迅速なシステム構築を行うことができるサービスも登場しています。その1つが kintone です。

ここでは、Salesforce で管理している顧客データ(取引先責任者)の情報を kintone に同期するサンプルアプリケーションを開発してみます。
CDataドライバーを利用すれば、こういったデータ同期を行うアプリケーションの作成を行うことができます。

![構成図.png](https://qiita-image-store.s3.amazonaws.com/0/141193/77e2e1f2-227f-4a52-e035-61aea82cba9a.png)

このアプリケーションの概要は次の通りです。

* Visual Studio 2015 C# による Windowsフォームアプリケーション
* GUIコンポーネントを駆使して可能な限りコーディングレスを目指す
* Salesforce および kintone からデータを読み込む
* インポートしたい行を選択して、Salesforce から kintone へデータをインポートする

それでは、このサンプルアプリケーションの開発手順を見ていきます。

## ソースコード
このサンプルアプリケーションのソースコードは[こちら](https://github.com/kasoorimethi/Salesforce2Kintone)からどうぞ。

※ソースコードについて補足
このソースコードでは接続情報をアプリケーションの設定に格納しています。
動作させる際は、アプリケーション設定のConnectionStringSalesforceにSalesforce用の接続文字列を、ConnectionStringKintoneにkintone用の接続文字列を設定してください。
それぞれの接続文字列は以下のようになります。
Salesforce → Password=<パスワード>;Security Token=<セキュリティトークン>;User=<ユーザ名>
kintone → Password=<パスワード>;Url=&lt;kintoneのURL>;User=<ユーザ名>

## CData製品のインストール
まずは CData Software をインストールします。今回必要になるのは、次の2つです。

* [Salesforce ADO.NET Provider](http://www.cdata.com/jp/drivers/salesforce/download/ado/ "Salesforce ADO.NET Provider")
* [Kintone ADO.NET Provider](https://www.cdata.com/jp/drivers/kintone/ado/ "Kintone ADO.NET Provider")

## プロジェクト作成
Visual Studio を起動し、新しいWindowsフォームアプリケーションのプロジェクトを作成します。


## 参照設定
インストールした CData Software の2製品には、いくつかのADO.NETアセンブリが含まれています。
今回必要になるのは次の2つのDLLですので、これらをプロジェクトの参照設定で追加します。

* System.Data.CData.Salesforce.dll
* System.Data.CData.Kintone.dll

![参照設定.png](https://qiita-image-store.s3.amazonaws.com/0/141193/18a8d9a4-ef73-8f09-dbaf-da47a2258956.png)

## データ接続を作成
Salesforce および kintone へのデータ接続を作成します。
以下のオンラインマニュアルを参考にして、2つのデータ接続を作成します。
[Salesforce用](http://cdn.cdata.com/help/GFB/ado/pg_connection.htm)
[kintone用](http://cdn.cdata.com/help/LKB/ado/pg_connection.htm)

![データ接続.png](https://qiita-image-store.s3.amazonaws.com/0/141193/f56891e9-0509-d22c-8375-535c37331d94.png)

## 実装
### フォームの作成
メインのフォームを追加します。
このフォームには、次のコンポーネントを追加します。

* データを表示するためのデータグリッドビュー×2 (左がSalesforce、右がkintoneと対応する)
* インポートを実行するボタン

![フォーム作成.png](https://qiita-image-store.s3.amazonaws.com/0/141193/92504151-8d8d-2511-e35f-e39c84d9635e.png)

### データグリッドビューのデータソースを設定

追加したデータグリッドビューに、データソースの設定を行います。
左側のデータグリッドビューで「DataGridView タスク」→「データソースの選択」→「プロジェクト データソースの追加」を選択します。

![DGVタスク.png](https://qiita-image-store.s3.amazonaws.com/0/141193/b585d5fb-9d84-55d9-100f-4cd478a9b2c7.png)

「データソースの種類の選択」では「データベース」を選択して「次へ」ボタンをクリックします。

![データソースの種類.png](https://qiita-image-store.s3.amazonaws.com/0/141193/47c84a5a-f93d-09bf-9df1-350f140c148a.png)

次の画面では「データセット」を選択して「次へ」ボタンをクリックします。

![データベースモデルの選択.png](https://qiita-image-store.s3.amazonaws.com/0/141193/ce209a3e-dbee-262e-5313-aac397eeaa23.png)

データ接続の選択では、[データ接続を作成](#データ接続を作成)で作成したSalesforceのデータ接続を選択して「次へ」ボタンをクリックします。

![データ接続の選択.png](https://qiita-image-store.s3.amazonaws.com/0/141193/95344d07-a8a9-1034-bed2-7e7e9e9bfd4d.png)

次の画面で接続文字列をアプリケーションに保存するかどうか選択できますが、ここでは任意に選択し、「次へ」ボタンをクリックします。

最後にデータベースオブジェクトの選択を行います。
今回はSalesforceの「取引先責任者」を対象とするので、Contactのチェックボックスにチェックを入れます。
データセット名は SalesforceDataSet として、「完了」ボタンをクリックします。

![データベースオブジェクトの選択.png](https://qiita-image-store.s3.amazonaws.com/0/141193/41ec86c6-d9cb-a07a-2168-fc3d1e46bb86.png)


同じようにして、kintone側もデータソースの設定を行います。
kintone側は、「顧客リスト」をデータベースオブジェクトとして選択します。
※kintoneのアプリが、ここにテーブルとして表示されます。
※このサンプルでは、顧客リストアプリに標準で存在する「会社ロゴ」の項目は削除しています。

![データベースオブジェクトの選択(KT).png](https://qiita-image-store.s3.amazonaws.com/0/141193/e2fa383c-ba16-a39b-62b7-9f59a0770f1a.png)

これで、データグリッドビューへのデータの読み込みまでが行えるようになりました。ここまで、全くコーディングは必要ありません。
メインフォームには、データ読み込みのためのコードが自動生成されています。

```csharp
// TODO: このコード行はデータを 'kintoneDataSet.顧客リスト' テーブルに読み込みます。必要に応じて移動、または削除をしてください。
this.顧客リストTableAdapter.Fill(this.kintoneDataSet.顧客リスト);
// TODO: このコード行はデータを 'salesforceDataSet.Contact' テーブルに読み込みます。必要に応じて移動、または削除をしてください。
this.contactTableAdapter.Fill(this.salesforceDataSet.Contact);
```

### 読み込むデータの修正

Salesforce側の会社名と会社住所は、取引先責任者の親である取引先の項目になるため、TableAdapter構成ウィザードを使用して、それらを取引先から取得するよう少し手を加えます。

先程のデータソースの構成で作成したデータセットを開き、「構成」メニューを選択します。

![データセットSF1.png](https://qiita-image-store.s3.amazonaws.com/0/141193/b684128f-27d7-911b-5094-f3ee09f80b42.png)

TableAdaoter構成ウィザードが開くので、ここで取引先(Account)から会社名と会社住所を取得するように修正を加えます。

![TableAdapter構成ウィザード.png](https://qiita-image-store.s3.amazonaws.com/0/141193/f02cc32f-c4cd-997a-928e-6ea92d7047b7.png)

これで、会社名と会社住所を取得できるようになりました。
データグリッドビューの「列の編集」から、これらの項目を追加しておきます。

### インポート処理
フォームデザイナで「インポート」ボタンをダブルクリックして、クリック時の処理を実装します。
ここでは、Salesforce側の取引先責任者のメールアドレスをkintone側のメールアドレスとひもづけ、これをキーとして取り扱うようにしています。

```csharp
private void btImport_Click(object sender, EventArgs e)
{
    //// エラー処理は省略

    this.btImport.Enabled = false;

    this.dgvSource.Rows.Cast<DataGridViewRow>()
        .Where(o => o.Selected)
        .Select((o, i) => new { Row = (SalesforceDataSet.ContactRow)((DataRowView)o.DataBoundItem).Row, Index = i })
        .ToList()
        .ForEach(selected =>
        {
            var row = this.kintoneDataSet.顧客リスト.Rows.Cast<KintoneDataSet.顧客リストRow>()
                .Where(o => o.メールアドレス == selected.Row.Email)
                .FirstOrDefault();

            if (row == null)
            {
                row = this.kintoneDataSet.顧客リスト.New顧客リストRow();
                row.RecordId = selected.Index
                    + (this.kintoneDataSet.顧客リスト.Rows.Cast<KintoneDataSet.顧客リストRow>().Max(o => (int?)o.RecordId) ?? 0)
                    + 1;
                row.Revision = 0;
            }

            row.メールアドレス = selected.Row.Email;
            row.会社名 = selected.Row.IsAccountNameNull() ? "" : selected.Row.AccountName;
            row.住所 = selected.Row.IsAccountAddressNull() ? "" : selected.Row.AccountAddress;
            row.担当者名 = selected.Row.IsNameNull() ? "" : selected.Row.Name;
            row.部署名 = selected.Row.IsDepartmentNull() ? "" : selected.Row.Department;
            row._郵便番号_数字のみ_ = selected.Row.IsAccountBillingPostalCodeNull() ? "" : selected.Row.AccountBillingPostalCode.Replace("-", "");
            row._TEL_数字のみ_ = selected.Row.IsPhoneNull() ? "" : selected.Row.Phone.Replace("-", "");
            row._FAX_数字のみ_ = selected.Row.IsFaxNull() ? "" : selected.Row.Fax.Replace("-", "");
            row.備考 = "";
            row.経度 = "";
            row.緯度 = "";

            if (row.Revision == 0)
            {
                this.kintoneDataSet.顧客リスト.Rows.Add(row);
            }

            this.顧客リストTableAdapter.Update(this.kintoneDataSet);
        });

    this.顧客リストTableAdapter.Fill(this.kintoneDataSet.顧客リスト);

    this.btImport.Enabled = true;
}
```

実装は以上です。

## 実行
アプリケーションを起動すると、データグリッドビューにデータが読み込まれます。

![アプリ起動後.png](https://qiita-image-store.s3.amazonaws.com/0/141193/9d6ed87c-a347-539c-e2e3-9cd2908c8e88.png)

移行したい行を選択して「インポート」ボタンをクリックすると、選択したデータが kintone に登録されます。

![アプリインポート後.png](https://qiita-image-store.s3.amazonaws.com/0/141193/d3027f28-9007-4087-8cdf-c00f22689290.png)

![インポート後(kintone).png](https://qiita-image-store.s3.amazonaws.com/0/141193/2ac6179b-6963-e701-df3b-7bfddbe2aa36.png)