4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

VisualStudio2017 で SalesForce の SOAP API を叩く(Enterprise版)

Last updated at Posted at 2019-04-25

やりたいこと

C# で SOAPAPI を利用して SalesForce 上のオブジェクトを操作
公式ドキュメントはこちら

Enterprise 版

  • 強い型付け
  • typo に強い
  • カスタムオブジェクトや項目が追加・更新された場合は再生成が必要

Partner 版の記事はこちら

事前準備

WSDL 追加

ブラウザでやること

・SalesForce 設定画面の左メニュー「ビルド - 開発 - API」で Enterprise WSDL の作成
・XMLが表示されたURLをコピー
※VSのweb参照追加画面からやるとJSエラーで表示できないため

VisualStudio でやること

参照追加

・ソリューションエクスプローラーで[追加]→[サービス参照]→[詳細設定]→[Web参照の追加]
・コピーしたURLを貼り付けるとSalseForceログイン画面が表示される
・ログインするとWSDLが表示されるので、適当なweb参照名(今回はsf_test)を付けて追加

Reference.cs修正

なんかそのままだとエラーが出る

エラー
System.InvalidOperationException
  HResult=0x80131509
  Message=一時クラスを生成できません (result=1)。
error CS0030: 型 'SFAPI.sf_test.ListViewRecordColumn[]' を型 'SFAPI.sf_test.ListViewRecordColumn' に変換できません。
error CS0030: 型 'SFAPI.sf_test.ListViewRecordColumn[]' を型 'SFAPI.sf_test.ListViewRecordColumn' に変換できません。
error CS0029: 型 'SFAPI.sf_test.ListViewRecordColumn' を型 'SFAPI.sf_test.ListViewRecordColumn[]' に暗黙的に変換できません。
error CS0029: 型 'SFAPI.sf_test.ListViewRecordColumn' を型 'SFAPI.sf_test.ListViewRecordColumn[]' に暗黙的に変換できません。

Reference.cs内のListViewRecordColumnに余計な[]が付いてるっぽいので削除(2箇所)

Reference.cs
//private ListViewRecordColumn[][] recordsField;    // 削除
private ListViewRecordColumn[] recordsField;        // 追加
Reference.cs
/// <remarks/>
[System.Xml.Serialization.XmlArrayItemAttribute("columns", typeof(ListViewRecordColumn), IsNullable=false)]
//public ListViewRecordColumn[][] records { // 削除
public ListViewRecordColumn[] records {     // 追加
    get {
        return this.recordsField;
    }
    set {
        this.recordsField = value;
    }
}

実装

ログイン/ログアウト

何はともあれログイン処理
テスト用に適当なWindowsフォームで実装
本来はログイン→なんらかのAPI処理→ログアウトまでをセットにした方がよさげ

Form1.cs
using SFAPI.sf_test;
using System;
using System.Web.Services.Protocols;
using System.Windows.Forms;

namespace SFAPI
{
    public partial class Form1 : Form
    {
        // bindingオブジェクト
        private SforceService binding;

        public Form1()
        {
            InitializeComponent();

            // 初期化
            binding = new SforceService();
            binding.Timeout = 30000;
        }

        /// <summary>
        /// ログインボタン
        /// </summary>
        private void LoginBtn_Click(object sender, EventArgs e)
        {
            Login();
        }

        /// <summary>
        /// ログイン処理
        /// </summary>
        /// <returns></returns>
        private bool Login()
        {
            string username = "SalesForceのログインID";
            string password = "SalesForceのパスワード";

            LoginResult loginResult;
            try
            {
                loginResult = binding.login(username, password);
            }
            catch (SoapException e)
            {
                // 失敗
                MessageBox.Show(e.Code + ":" + e.Message);
                return false;
            }

            if (loginResult.passwordExpired)
            {
                // パスワード失効
                // binding.setPassword(userId, newPassword) で更新が必要
                return false;
            }

            // エンドポイント更新
            string authEndPoint = binding.Url;      // 初期化時のログイン認証URL
            binding.Url = loginResult.serverUrl;    // 認証後のサービス用URL

            // セッションヘッダ追加
            binding.SessionHeaderValue = new SessionHeader();
            binding.SessionHeaderValue.sessionId = loginResult.sessionId;


            // 結果表示
            GetUserInfoResult userInfo = loginResult.userInfo;

            string msg = "";
            msg += "UserID: " + userInfo.userId + "\r\n";
            msg += "User Full Name: " + userInfo.userFullName + "\r\n";
            msg += "User Email: " + userInfo.userEmail + "\r\n";
            msg += "\r\n";
            msg += "SessionID: " + loginResult.sessionId + "\r\n";
            msg += "Auth End Point: " + authEndPoint + "\r\n";
            msg += "Service End Point: " + loginResult.serverUrl + "\r\n";

            MessageBox.Show(msg);

            return true;
        }

        /// <summary>
        /// ログアウトボタン
        /// </summary>
        private void LogoutBtn_Click(object sender, EventArgs e)
        {
            Logout();
        }

        /// <summary>
        /// ログアウト
        /// </summary>
        private bool Logout()
        {
            try
            {
                binding.logout();
            }
            catch (SoapException e)
            {
                MessageBox.Show(e.Message);
                return false;
            }
            return true;
        }
    }
}

オブジェクト操作

例として Contact の操作

Read.cs
/// <summary>
/// Contact取得
/// </summary>
private void getContacts()
{
    // SOQL発行
    // リレーション項目も取得可能(取引先担当者が所属する取引先名Account.Name)
    String soqlQuery = "SELECT Id, FirstName, LastName, Account.Name FROM Contact";
    try
    {
        // デフォルトだと500件までしか取れない
        QueryResult qr = binding.query(soqlQuery);

        string msg = "";
        while (true)
        {
            sObject[] records = qr.records;
            for (int i = 0; i < records.Length; i++)
            {
                Contact con = (Contact)records[i];
                msg += con.Id + ":" + con.LastName + " " + con.FirstName + " : " + con.Account.Name + "\r\n";
            }

            if (qr.done)
            {
                // おわり
                break;
            }
            else
            {
                // 500件以上存在する場合、次の500件を取得
                qr = binding.queryMore(qr.queryLocator);
            }
        }

        MessageBox.Show(msg);
    }
    catch (Exception ex)
    {
        // エラー
        MessageBox.Show(ex.Message);
    }
}
Upsert.cs
/// <summary>
/// Contact更新
/// </summary>
public void upsertContacts()
{
    // 更新対象配列作成(テストなので適当)
    sObject[] upserts = new Contact[1];

    Contact c0 = new Contact();
    c0.Email = "test@example.com";
    c0.LastName = "てすと";
    c0.FirstName = "たろう";
    c0.AccountId = "001000000000000";

    // カスタム項目を扱う場合、指定フラグを立てる必要がある
    c0.CustomDate__cSpecified = true;
    c0.CustomDate__c = System.DateTime.Now;
    
    upserts[0] = c0;

    try
    {
        // Email一致の場合UPDATE、それ以外の場合INSERT
        UpsertResult[] upsertResults = binding.upsert("Email", upserts);

        string msg = "";

        foreach (UpsertResult result in upsertResults)
        {
            if (result.success)
            {
                msg += result.id + " : " + (result.created ? "Insert" : "Update") + "\r\n";
            }
            else
            {
                MessageBox.Show("Error!: " + result.errors[0].message);
            }
        }

        MessageBox.Show(msg);
    }
    catch (SoapException e)
    {
        MessageBox.Show(e.Message);
    }
}

おわりに

Reference.cs 修正しないと動かないのが謎。
別サービスでも似たようなことがあったし流行ってるんですかね…

カスタムオブジェクトや項目の追加、変更があった場合、
WSDL 読み直しになるから面倒。

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?