Salesforce と SendGrid でコンタクトを同期する方法

現代、Eメールはもはや業務にかかせないインフラの1つとなりました。Eメールはビジネスの場において必須のツールですが、そんなメール配信も今や SaaS の時代です。
メール配信サービスの1つに SendGrid があります。SendGrid を利用すれば、顧客や見込み客に一斉にメール送信を行うことができます。ですが、SendGrid はあくまでメール配信サービスであり、顧客の管理自体は別のシステムで行うことになります。そのため、顧客を管理しているシステムから SendGrid へデータを同期できると便利です。

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

![構成図.png](https://qiita-image-store.s3.amazonaws.com/0/141193/1ca16d52-ec55-a5d7-c972-4e7bd99a658d.png)

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

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

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

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

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

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

* [Salesforce ADO.NET Provider](http://www.cdata.com/jp/drivers/salesforce/download/ado/ "Salesforce ADO.NET Provider")
* [SendGrid ADO.NET Provider](https://www.cdata.com/drivers/sendgrid/ado/ "SendGrid ADO.NET Provider") ※2016/12現在、英語β版のみの提供

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


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

* System.Data.CData.Salesforce.dll
* System.Data.CData.SendGrid.dll

![参照設定.png](https://qiita-image-store.s3.amazonaws.com/0/141193/9a556e14-7783-5efd-5ffd-195f635a45cf.png)


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

![データ接続.png](https://qiita-image-store.s3.amazonaws.com/0/141193/2be008c8-da8d-f765-fdf7-d865b719d71c.png)

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

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

![フォーム作成.png](https://qiita-image-store.s3.amazonaws.com/0/141193/86955cbe-62bd-43e5-c923-8654153df1a7.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)

同じようにして、SendGrid側もデータソースの設定を行います。
SendGrid側は、Recipientsをデータベースオブジェクトとして選択します。

![データベースオブジェクトの選択(SG).png](https://qiita-image-store.s3.amazonaws.com/0/141193/2593f95c-d1de-28ec-8a3d-60061ca5016e.png)

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

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

### インポート処理
フォームデザイナで「インポート」ボタンをダブルクリックして、クリック時の処理を実装します。

```csharp
private void btImport_Click(object sender, EventArgs e)
{
    var sendGridMap = this.dgvDestination.Rows
        .Cast<DataGridViewRow>()
        .Select(o => (SendGridDataSet.RecipientsRow)((DataRowView)o.DataBoundItem).Row)
        .ToDictionary(o => o.Email, o => o);

    var selectedList = this.dgvSource.Rows.Cast<DataGridViewRow>()
        .Where(o => o.Selected)
        .Select(o => (SalesforceDataSet.ContactRow)((DataRowView)o.DataBoundItem).Row)
        .ToList();


    this.btImport.Enabled = false;

    Task.Factory.StartNew(() =>
    {
        var processed = 0;

        foreach (var selected in selectedList)
        {
            Invoke(new Action(() => this.Text = "処理中... (" + (processed + 1) + " / " + selectedList.Count + ")" ));

            if (sendGridMap.ContainsKey(selected.Email) || selected.IsEmailNull())
            {
                continue;
            }
            
            this.recipientsTableAdapter.Insert(
                DBNull.Value, DBNull.Value, DBNull.Value,
                selected.Email, selected.IsFirstNameNull() ? "" : selected.FirstName, selected.LastName,
                DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value);

            processed++;
        }
    })
    .ContinueWith(task =>
    {
        // エラー処理は省略

        Invoke(new Action(() =>
        {
            this.btImport.Enabled = true;
            this.Text = "完了";
        }));
    });
}
```

リロードのボタンの処理も実装しておきましょう。
リロードボタンの処理はたったこれだけです。

```csharp
private void btReload_Click(object sender, EventArgs e)
{
   this.recipientsTableAdapter.Fill(this.sendGridDataSet.Recipients);
}
```

実装は以上です。

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

![アプリ起動後.png](https://qiita-image-store.s3.amazonaws.com/0/141193/fabc4674-eca1-35eb-9f37-e9b663846333.png)

移行したい行を選択して「インポート」ボタンをクリックすると、選択したデータが SendGrid に登録されます。
ただ、SendGridにデータが反映されるまで少し時間がかかるようです。少し待ってからリロードボタンでデータをリロードし、データが登録されたことを確認します。

![アプリインポート後.png](https://qiita-image-store.s3.amazonaws.com/0/141193/3cdaccf3-e95a-226c-2f34-017b34b4a0cc.png)

![インポート後SG画面.png](https://qiita-image-store.s3.amazonaws.com/0/141193/6304b195-c58e-1059-3f3b-f64664f6fcf3.png)