SalesforceでSOAP APIを使ったレコードの削除

Salesforce APIを使ったレコードの削除方法を紹介します。

Salesforce SOAP APIシリーズも第4回目を迎えます。第1回目の「SalesforceでSOAP APIを使ったデータ読み込み」、第2回目の「SalesforceでSOAP APIを使ったレコードの作成」、第3回目の「SalesforceでSOAP APIを使ったレコードの更新」に引き続き、今回はレコードの削除をお届けします。

第2回目で作成し、第3回目で更新した商談レコードを今回は削除していまします。

レコード削除メソッド

それでは、まずは削除メソッドを作っていきます。

public void Delete(List<string> list)
{
	try
	{
		var deleteList = new List<string>();
		list.ForEach(item =>
		{
			deleteList.Add(item);
			if (deleteList.Count == 200)
			{
				DeleteObjects(deleteList);
				deleteList = new List<string>();
			}
		});
		if (deleteList.Count > 0)
		{
			DeleteObjects(deleteList);
		}
	}
	catch (SoapException)
	{
		throw;
	}
	catch (Exception)
	{
		throw;
	}
}

private void DeleteObjects(List<string> deleteList)
{
	var results = binding.delete(deleteList.ToArray());
	var errors = results.ToList().FindAll(result => !result.success);
	if (errors.Count > 0)
	{
		foreach (var error in errors)
		{
			string message = string.Empty;
			error.errors.ToList().ForEach(e =>
			{
				message += e.message + System.Environment.NewLine;
			});
			System.Diagnostics.Debug.WriteLine("ID: {0}, Message: {1}", error.id, message);
		}
	}
}

今回、注目は取り扱うデータの型です。取得、作成、更新のときはsObject型を扱っていましたが、今回はstring型です。というのも、deleteメソッドはstring配列を渡せばよく、string配列にはSalesforce IDの配列を入れてあげればいいだけだからです。

逆にいうと、あらかじめIdがわかっているか、削除対象のIdを取得してこなければいけないことになります。取得してくる場合は第1回目のときのGetメソッドが使えます。

メインの削除処理

メインの処理では商談レコードから削除したいデータを取得して、Idだけを取り出し、string配列にしてDeleteメソッドに渡してあげます。

using (var salesforce = new SalesforceAccess(userName, password, securityToken))
{
	if (!salesforce.Login()) return;
	var opportunities = salesforce.Get<Opportunity>("SELECT Id FROM Opportunity WHERE Name LIKE '商談の名前%'");
	var list = new List<string>();
	opportunities.ForEach(opportunity => list.Add(opportunity.Id));

	salesforce.Delete(list);
}

GetメソッドでSOQLを書いていますが、抽出する項目はIdのみを指定しています。Idしか必要がないからです。

なお、削除したレコードはゴミ箱に入ります。ちなみに、Lightning Experienceではゴミ箱は使えません。ゴミ箱にアクセスするにはSalesforce Classicに切り替える必要があります1。ただ、切り替えなくてもドメイン名のうしろに「/search/UndeletePage」とURL直打ちで一時的にゴミ箱だけクラシック画面でアクセスできます。 なんなら、ゴミ箱のURLをお気に入りに入れておけばいいですね。

完成形コード

完成形のコードを紹介します。まずはMainメソッドの処理からです。

using System;
using System.Collections.Generic;
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 FROM Opportunity WHERE Name LIKE '商談の名前%'");
				var list = new List<string>();
				opportunities.ForEach(opportunity => list.Add(opportunity.Id));

				salesforce.Delete(list);
			}
		}
	}
}

Mainメソッドの中でGetメソッドを使って削除対象のデータを取得して、stringの配列にしてからDeleteメソッドを呼び出しています。これは、上で説明した通りです。

次にDeleteメソッドが加わったSalesforceAccessクラスです。

using System;
using System.Linq;
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: Sforce.sObject
		{
			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;
		}

		public void Create<T>(List<T> list) where T : sObject
		{
			try
			{
				var createList = new List<T>();
				list.ForEach(item =>
				{
					createList.Add(item);
					if (createList.Count == 200)
					{
						CreateObjects(createList);
						createList = new List<T>();
					}
				});
				if (createList.Count > 0)
				{
					CreateObjects(createList);
				}
			}
			catch (SoapException)
			{
				throw;
			}
			catch (Exception)
			{
				throw;
			}
		}

		private void CreateObjects<T>(List<T> createList) where T : sObject
		{
			var results = binding.create(createList.ToArray());
			var errors = results.ToList().FindAll(result => !result.success);
			if (errors.Count > 0)
			{
				foreach (var error in errors)
				{
					string message = string.Empty;
					error.errors.ToList().ForEach(e =>
					{
						message += e.message + System.Environment.NewLine;
					});
					System.Diagnostics.Debug.WriteLine("ID: {0}, Message: {1}", error.id, message);
				}
			}
		}

		public void Update<T>(List<T> list) where T: sObject
		{
			try
			{
				var updateList = new List<T>();
				list.ForEach(item =>
				{
					updateList.Add(item);
					if (updateList.Count == 200)
					{
						UpdateObjects(updateList);
						updateList = new List<T>();
					}
				});
				if (updateList.Count > 0)
				{
					UpdateObjects(updateList);
				}
			}
			catch (SoapException)
			{
				throw;
			}
			catch (Exception)
			{
				throw;
			}
		}

		private void UpdateObjects<T>(List<T> updateList) where T : sObject
		{
			var results = binding.update(updateList.ToArray());
			var errors = results.ToList().FindAll(result => !result.success);
			if (errors.Count > 0)
			{
				foreach (var error in errors)
				{
					string message = string.Empty;
					error.errors.ToList().ForEach(e =>
					{
						message += e.message + System.Environment.NewLine;
					});
					System.Diagnostics.Debug.WriteLine("ID: {0}, Message: {1}", error.id, message);
				}
			}
		}

		public void Delete(List<string> list)
		{
			try
			{
				var deleteList = new List<string>();
				list.ForEach(item =>
				{
					deleteList.Add(item);
					if (deleteList.Count == 200)
					{
						DeleteObjects(deleteList);
						deleteList = new List<string>();
					}
				});
				if (deleteList.Count > 0)
				{
					DeleteObjects(deleteList);
				}
			}
			catch (SoapException)
			{
				throw;
			}
			catch (Exception)
			{
				throw;
			}
		}

		private void DeleteObjects(List<string> deleteList)
		{
			var results = binding.delete(deleteList.ToArray());
			var errors = results.ToList().FindAll(result => !result.success);
			if (errors.Count > 0)
			{
				foreach (var error in errors)
				{
					string message = string.Empty;
					error.errors.ToList().ForEach(e =>
					{
						message += e.message + System.Environment.NewLine;
					});
					System.Diagnostics.Debug.WriteLine("ID: {0}, Message: {1}", error.id, message);
				}
			}
		}
	}
}

SalesforceAccessクラスの完成形と言えるものです。ログイン、ログアウト処理、データの取得、作成、更新、削除の処理が網羅された必要十分なクラスです。このクラスがあれば、ほとんどのシステムで用が足りると思います。

まとめ

削除処理ができれば、不要なデータを削除するよう定期的に実行するバッチなんかも組めますので、データストレージの容量対策に古いデータから削除するということも可能になります。

Salesforce SOAP APIシリーズ第4回目でデータの削除を紹介しました。そして、今回の記事の掲載しているSalesforceAccessクラスは、Salesforceとのデータ連携に必要な処理が網羅されていますので、これがあればだいたいのことができるようになります。

あとは、業務に合わせてデータを取得したり、加工して更新したり、削除したりすればいいわけです。例えば、社内のシステムで見積もり処理をしたり、受注の処理を管理しているのであれば、金額や原価などのデータをSalesforceと同期して上げることで、営業担当の入力負荷を軽減することができます。

また、既存のシステムからリプレースする場合、SOAP APIを使ってマイグレーションツールを作成すれば、データ移行をする際に大活躍することになるでしょう。APIを整備して、広く解放しているSalesforce、というかforce.comというプラットフォームだからできることです。

APIを活用して、より効率的にシステムの運用を実現することができます。

  1. 記事作成時点での情報です。データアクセスおよびビュー: Lightning Experience の制限事項より。

Follow me!

Feedlyで新着記事をチェックしよう!

Feedlyでフォローしておけば、新着記事をチェックすることができます。ぜひ、この機会にFeedlyに追加しておきましょう。