Salesforce と SharePoint 間でカレンダーを同期する方法

このご時世、行動予定をクラウド上で管理するということはもう大分浸透していると思いますが、基本的な機能のため、様々な SaaS で実装されている機能でもあります。利用している複数の SaaS に行動予定やカレンダーのような機能が備わっているとしたら、それらを同期しなければなりません。

CDataドライバーを利用すれば、こういったデータ同期を行うアプリケーションの作成を行うことができます。

![構成図.png](https://qiita-image-store.s3.amazonaws.com/0/141193/4823f46f-dc43-9f18-6b8b-1303e2cb7443.png)

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

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

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

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

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

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

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

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

![プロジェクト作成.png](https://qiita-image-store.s3.amazonaws.com/0/141193/b6717772-8e61-6688-cd08-13d88b63bd4a.png)

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

* System.Data.CData.Salesforce.dll
* System.Data.CData.SharePoint.dll

![参照設定.png](https://qiita-image-store.s3.amazonaws.com/0/141193/cebc83b3-867d-84b1-55db-f30591e87d75.png)

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

![データ接続.png](https://qiita-image-store.s3.amazonaws.com/0/141193/d99adb56-728a-7c3c-180f-3e8f22f5ab91.png)

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

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

![フォーム作成.png](https://qiita-image-store.s3.amazonaws.com/0/141193/df62293e-c6a9-c137-b192-d13f6f230ce4.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の「行動」を対象としますので、Eventのチェックボックスにチェックを入れます。
データセット名は SalesforceDataSet として、「完了」ボタンをクリックします。

![データベースオブジェクトの選択.png](https://qiita-image-store.s3.amazonaws.com/0/141193/96244491-9bd4-7592-6efb-88f0783cc142.png)

同じようにして、SharePoint側もデータソースの設定を行います。
SharePoint側は、作成済みのカレンダー(ここでは"Salesforce予定表")をデータベースオブジェクトとして選択します。

![データベースオブジェクトの選択(SP).png](https://qiita-image-store.s3.amazonaws.com/0/141193/1ee8000b-dae0-668a-d556-75b930a528e8.png)

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

```csharp
// TODO: このコード行はデータを 'sharePointDataSet.Salesforce予定表' テーブルに読み込みます。必要に応じて移動、または削除をしてください。
this.salesforce予定表TableAdapter.Fill(this.sharePointDataSet.Salesforce予定表);
// TODO: このコード行はデータを 'salesforceDataSet.Event' テーブルに読み込みます。必要に応じて移動、または削除をしてください。
this.eventTableAdapter.Fill(this.salesforceDataSet.Event);
```

※ワンポイント
SharePointのAPIの仕様で、デフォルトでは30日分のデータが取得されます。これをもっと広い期間にしたい場合は、SharePointDataSet.xsdデザイナーで開く → Salesforce予定表を右クリック → 「構成」から構成ウィザードを開く とSQLステートメントを入力できるので、次のようなWHERE句を指定します。

```sql
WHERE [開始時刻] >= '2015-10-01' AND [終了時刻] <= '2017-10-01'
```

### インポート処理
フォームデザイナで「インポート」ボタンをダブルクリックして、クリック時の処理を実装します。
ここでは、件名が同じであれば同じデータであると判断します。

```csharp
private void btImport_Click(object sender, EventArgs e)
{
    var sharePointMap = this.dgvDestination.Rows
        .Cast<DataGridViewRow>()
        .Select(o => (SharePointDataSet.Salesforce予定表Row)((DataRowView)o.DataBoundItem).Row)
        .ToDictionary(o => o.タイトル, o => o);


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

    var dummyId = 0;

    foreach (var selected in selectedList)
    {
        if (sharePointMap.ContainsKey(selected.Subject))
        {
            continue;
        }

        var newRow = this.sharePointDataSet.Salesforce予定表.NewSalesforce予定表Row();

        newRow.ID = dummyId++;
        newRow.タイトル3 = selected.Subject;
        newRow.開始時刻 = selected.StartDateTime;
        newRow.終了時刻 = selected.EndDateTime;
        newRow.場所 = selected.IsLocationNull() ? "" : selected.Location;
        newRow.終日 = selected.IsAllDayEvent ? "1" : "0";
        newRow.説明 = selected.IsDescriptionNull() ? "" : selected.Description;

        this.sharePointDataSet.Salesforce予定表.AddSalesforce予定表Row(newRow);
    }

    this.salesforce予定表TableAdapter.Update(this.sharePointDataSet.Salesforce予定表);
    this.salesforce予定表TableAdapter.Fill(this.sharePointDataSet.Salesforce予定表);
}
```

実装は以上です。

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

![アプリ起動後.png](https://qiita-image-store.s3.amazonaws.com/0/141193/b0a99ac6-c247-0724-4594-fb50005c732f.png)

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

![アプリインポート後.png](https://qiita-image-store.s3.amazonaws.com/0/141193/f227e800-639e-698e-7eb6-8ac2f018b546.png)

![SP画面.png](https://qiita-image-store.s3.amazonaws.com/0/141193/463e0ba9-01c9-3ac0-ea0d-1d036b472b02.png)