SalesforceでSOAP APIを使ったデータ読み込み
C#などの主要な言語でSalesforceのAPIを使ったデータのやり取りを行う方法を紹介します。
SalesforceではSOAP APIを使って簡単にデータの取得、更新ができます。使い慣れたC#などのプログラミング言語を使い、社内のシステムとのデータ連携などが可能です。
例えば、基幹システムなどで管理している売上データをSalesforceの商談に反映させるときなど、APIを使って同期するプログラムを組んだりできます。
Enterprise WSDLの生成
もしVisual Studioをメインの開発環境としているならラッキーです。Salesforceで生成したWSDLのXMLファイルをVisual Studioに読み込むだけで、Salesforceのオブジェクトをクラスとして利用できるようになります。
設定の「カスタムコード」→「API」と進むと「Enterprise WSDL」とありますので生成のリンクをクリックします。
生成されたXMLファイルを保存します。保存するときはブラウザーによっては「Webアーカイブ形式」を選択できますが、ソースコード(単一のファイル)の形式で保存します。
System.Web.Serviceが必要なので、System.Web.Serviceを参照で追加できるテンプレートをプロジェクトで選びます。ここでは「その他」から「コンソール プロジェクト」を選んでおきます1。
プロジェクトを右クリックして「追加」から「Web 参照の追加」を選びます。
ダウンロードしたXMLファイルを指定します。ローカルファイルのURL形式がわからなければ、ブラウザーなどに一度ドラッグしてアドレスバーのURLをコピペするといいです。
参照のところに「Sforce」と入力しておきます2。
セキュリティトークンを作ろう
他のアプリケーションからログインする場合、セキュリティトークンがないとログインではじかれます。なので、セキュリティトークンを作成しておきましょう。
右上の歯車アイコンの「設定」から「私の個人情報」→「私のセキュリティトークンのリセット」に行き、[セキュリティトークンのリセット] ボタンをクリックします。
メールでセキュリティトークンが飛んできますので、それを使ってログインします。
ログイン
ログインする場合、ユーザ名とパスワードにセキュリティトークンを付与した文字列をloginメソッドに渡してあげるだけです。
public bool Login()
{
binding = new SforceService();
binding.Timeout = 60000;
LoginResult loginResult;
try
{
loginResult = binding.login(userName, password + securityToken);
}
catch (SoapException)
{
throw;
}
catch (Exception)
{
throw;
}
if (loginResult.passwordExpired)
{
return false;
}
binding.Url = loginResult.serverUrl;
binding.SessionHeaderValue = new SessionHeader();
binding.SessionHeaderValue.sessionId = loginResult.sessionId;
return true;
}
例外処理は面倒なのでthrowしているけど、実環境ではここでちゃんとメッセージを出すなりログに吐くなりしておきましょう。
パスワード期限切れは別途passwordExpiredで判断して処理してあげる必要があります。インテグレーション用のアプリケーション、もしくはサービスで使用するパスワードは無期限にしておくといいと思います。
ログアウト
ログアウトの処理は、ただログインに使用したSforceServiceインスタンスでlogoutメソッドを実行してあげるだけです。
public void Logout()
{
try
{
binding.logout();
isLogin = false;
}
catch (SoapException)
{
throw;
}
catch (Exception)
{
throw;
}
}
こちらの例外処理も、本当は同じようにメッセージやログなどでエラーが発生したことを残すようにしておくといいでしょう。
データを取得してみよう
データを取得する部分ですが、Salesforceのオブジェクトのクラスごとに処理を作ってもいいのですが、ジェネリックのメソッドとして作り、その都度SOQL3を引数で渡してあげるほうがコードがシンプルかなと個人的には思います。どちらでもいいと思いますけどね。
public List<T> Get<T>(string query) where T: class
{
var list = new List<T>();
QueryResult queryResult;
try
{
queryResult = binding.query(query);
bool done = false;
if (queryResult.size > 0)
{
while (!done)
{
var records = queryResult.records;
for (int i = 0; i < records.Length; i++)
{
var recordType = records[i] as T;
list.Add(recordType);
}
if (queryResult.done) done = true;
else queryResult = binding.queryMore(queryResult.queryLocator);
}
}
}
catch (SoapException)
{
throw;
}
catch (Exception)
{
throw;
}
return list;
}
例外処理はテキトー仕様ですので、実運用に使う場合はちゃんとメッセージを出すなりログを吐くなりしましょう。
完成形コード
以下に完成形のコードを紹介します。Salesforceにログイン後、商談オブジェクトのIDとNameを取得してコンソールに表示するだけのプログラムです。
using System;
using System.Configuration;
using SalesforceAccessConsoleApp.Sforce;
namespace SalesforceAccessConsoleApp
{
class MainClass
{
public static void Main(string[] args)
{
string userName = ConfigurationManager.AppSettings["SalesforceUserName"];
string password = ConfigurationManager.AppSettings["SalesforcePassword"];
string securityToken = ConfigurationManager.AppSettings["SalesforceSecurityToken"];
using (var salesforce = new SalesforceAccess(userName, password, securityToken))
{
if (!salesforce.Login()) return;
var opportunities = salesforce.Get<Opportunity>("SELECT Id, Name FROM Opportunity");
foreach (var opportunity in opportunities)
{
Console.WriteLine("ID: {0}, Name: {1}", opportunity.Id, opportunity.Name);
}
}
}
}
}
メインのコードです。ここでコンソールへの出力処理を実施しています。ここでは、ユーザ名、パスワード、セキュリティトークンはコード上で見せないように設定ファイルから取得しています。
以下がSalesforceにアクセスするためのメソッドをまとめたクラスです。
using System;
using System.Collections.Generic;
using System.Web.Services.Protocols;
using SalesforceAccessConsoleApp.Sforce;
namespace SalesforceAccessConsoleApp
{
public class SalesforceAccess : IDisposable
{
SforceService binding;
string userName;
string password;
string securityToken;
bool isLogin = false;
public SalesforceAccess(string loginUserName, string loginPassword, string loginSecurityToken)
{
userName = loginUserName;
password = loginPassword;
securityToken = loginSecurityToken;
}
public void Dispose()
{
Logout();
}
public bool Login()
{
binding = new SforceService();
binding.Timeout = 60000;
LoginResult loginResult;
try
{
loginResult = binding.login(userName, password + securityToken);
}
catch (SoapException)
{
throw;
}
catch (Exception)
{
throw;
}
if (loginResult.passwordExpired)
{
return false;
}
binding.Url = loginResult.serverUrl;
binding.SessionHeaderValue = new SessionHeader();
binding.SessionHeaderValue.sessionId = loginResult.sessionId;
isLogin = true;
return true;
}
public void Logout()
{
if (!isLogin) return;
try
{
binding.logout();
isLogin = false;
}
catch (SoapException)
{
throw;
}
catch (Exception)
{
throw;
}
}
public List<T> Get<T>(string query) where T: class
{
var list = new List<T>();
QueryResult queryResult;
try
{
queryResult = binding.query(query);
bool done = false;
if (queryResult.size > 0)
{
while (!done)
{
var records = queryResult.records;
for (int i = 0; i < records.Length; i++)
{
var recordType = records[i] as T;
list.Add(recordType);
}
if (queryResult.done) done = true;
else queryResult = binding.queryMore(queryResult.queryLocator);
}
}
}
catch (SoapException)
{
throw;
}
catch (Exception)
{
throw;
}
return list;
}
}
}
好みの問題ですが、usingで使えるようにIDisposableというインターフェースを指定しています。そして、Disposeメソッドを実装して、その中でLogoutメソッドをしています。if文でログインして、trueが返ってきたら処理を実行し、if文を抜けるときにLogoutを実行する方法もあります。どっちでもいいと思いますけどね。
まとめ
Salesforceと外部システムとのインテグレーションは、こんなに簡単に組むことができます。そこそこのシステム開発経験があれば、簡単なデータ同期処理なら別途ベンダーに依頼しなくてもいいレベルです。
例えば、Windows Serverを1台用意して、データのやりとりをサービスアプリケーションとして組んでおけば、定期的に同期処理を実行してSalesforceと外部システム(例えば、社内のシステム)とデータ連携をすることも可能です4。サービスアプリケーションじゃなくても、バッチプログラムとタスクスケジューラーを使ってもいいですね。
また、定期的にSalesforceからデータを取得して何か処理したい場合などに、プログラムを組んでおけば、データローダーを使った場合と比べると、取得するオブジェクトの種類などをUIで指定しなくてもいいので効率的ですね。
- あとで参照からSystem.Web.Serviceを選んでいます。 ↩
- APIリファレンスがSforceを使っているので、それに倣ってSforceとしておきます。ここはなんでもいいと思います。 ↩
- Salesforce Object Query Languageの頭文字をとったもので、Salesforceのオブジェクト用のSQL的なもの。 ↩
- Windows Serverじゃなくてもいいけどね。 ↩