CrmSer ice 経由で エンティティのレコードを作成すると 既定では重複チェックが行われません。エンティティが重複チェックを行うように設定されかつ、重複データの演出ルールが定義されていたとしても、データは登録、更新できます。

Windows Live版ブログDynamics CRM の CrmService API で データ登録時に重複データ検出ルールを使用するで紹介した OptionParameter のサブクラスである、CreateDuplicatesOptionalParameter と PersistInSyncOptionalParameter を使用して重複データの検出を行うようにできます。重複データが見つかった場合例外SoapException がスローされるようになります。

 動作確認は Dynamics CRM 4.0 Rollup 8 が適用された環境で行っています。プログラムは Visual Studio 2008(.NET 3.5)でコンパイルしていますが.NET 3.0 以上の機能はつかっていないのでSystem.Linq などのusingを削除すれば.NET 2.0 以上で動作できると思います。

今回は、Dynamics CRM インストール時に既定で作成される 取引先企業の重複ルール(メールアドレス重複) に一致するデータを連続で投入して重複エラーが発生することを確認します。

1. CrmService経由でエンティティ作成時に重複データを検出するサンプル

1.1 サンプルプログラム

デモプログラムを以下に記載します。サンプルプログラムでは、CreateDuplicatesOptionalParameter(false) とすることで重複データの作成(更新)を行うときに重複データが存在したばあエラーとするように指定しています。また、PersistInSyncOptionalParameter(true) とすることで同期的にマッチコードの更新を行うようにしています。マッチコードについてはサンプログラムの後に記載します。

サンプルプログラムをコンパイルするには、 microsoft.crm.sdk, microsoft.crm.sdktypeproxy.dll, System.Web.Services.dll への参照を追加する必要があります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk;
using System.Net;
using System.Web.Services.Protocols;
using System.Xml;

namespace CrmServiceDuplicateCheck
{
    /// <summary>
    /// CreateDuplicatesOptionalParameter を使って、 CrmServiceのCreate,Update メソッド時に
    /// 重複データ検出ルールを使用して重複データ投入時にエラーにするサンプル
    /// 
    /// サンプルプログラムでは Dynamics にデフォルトで用意されている 取引先企業の重複ルール
    /// に一致するデータを投入する.
    /// 
    /// microsoft.crm.sdk.dll,microsoft.crm.sdktypeproxy.dll,System.Web.Services.dll への参照を追加すること
    /// 
    /// キーワード:マッチコードの更新システムジョブ。画面から連続して重複したデータを投入しても
    /// エラーが発生しないのは、マッチコードの更新システムジョブが5分ごとにしか実行されない
    /// ためである。
    /// マッチコードの更新も同期的に行うとパフォーマンスに影響が発生するので、注意してください。
    /// </summary>
    class Program
    {
        /// <summary>
        /// ○まとめ
        /// CreateDuplicatesOptionalParameter.Valule 
        /// true : 重複データが見つが存在してもデータの登録、更新を行う。(既定)
        /// false: 重複データが見つかった場合、データの登録、更新は行わない。SoapExceptionが発生
        /// 
        /// PersistInSyncOptionalParameter.Value
        /// true : マッチコードの更新を同期的に行う。(直後に重複データが登録・更新されても検知可能)
        /// false: マッチコードの更新を非同期で行う。(直後に重複データが登録・更新されても検知負荷)(既定)
        /// 
        /// 非同期マッチコードの更新の場合、システムジョブのマッチコードの更新ジョブが実行されるまで
        /// 重複データの検出は行われない。(5分ごとにジョブは実行される)
        /// 
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            try
            {
                CrmService service = GetCrmService("localhost", "dyn");

                // 同じメールアドレスで企業作成
                CreateAccount(service, "取引先企業C1", "test@test36.com");
                CreateAccount(service, "取引先企業D1", "test@test36.com");
            }
            catch (SoapException se)
            {
                XmlNode code = se.Detail.SelectSingleNode("//code");
                if (code != null)
                {
                    Console.WriteLine("Error Code:" + code.InnerText);
                }
                XmlNode description = se.Detail.SelectSingleNode("//description");
                if (description != null)
                {
                    Console.WriteLine("Error Message:" + description.InnerText);
                }
                //Console.WriteLine(se.Detail.InnerXml);
            }
            Console.WriteLine("終了");
            Console.ReadLine();
        }
        /// <summary>
        /// 登録時に同期的にデータの重複チェックを行い、重複ルールに一致するものがあれば
        /// エラーとする。
        /// 取引先企業の重複ルールでは、emailaddress1,emailaddress2,emailaddress3 がすべて
        /// 一致するとエラーとなる。ただしブランク(NULL)どうしの項目一致とみなされるので注意
        /// </summary>
        /// <param name="service"></param>
        /// <param name="name"></param>
        /// <param name="email"></param>
        static void CreateAccount(CrmService service, string name, string email)
        {
            account account1 = new account();
            account1.name = name;
            account1.emailaddress1 = email;
            //account1.emailaddress2 = email;
            //account1.emailaddress3 = email;

            CreateRequest request = new CreateRequest();
            TargetCreateAccount target = new TargetCreateAccount();
            target.Account = account1;
            // PersistInSyncOptionalparameter(false)の場合、非同期でマッチコードの更新が行われる
            // そのため、連続して重複ルールに一致するデータを投入するとエラーとならない。
            request.OptionalParameters = new OptionalParameter[]{ new CreateDuplicatesOptionalParameter(false),
                                                                    new PersistInSyncOptionalParameter(true)};
            request.Target = target;

            Response response =  service.Execute(request);
        }

        /// <summary>
        /// OnPremise (AD) 認証 環境でテスト
        /// </summary>
        /// <param name="host">CRMサーバ名</param>
        /// <param name="orgName">組織名</param>
        /// <returns></returns>
        static CrmService GetCrmService(string host, string orgName)
        {
            CrmService service = new CrmService();

            UriBuilder uriBuilder = new UriBuilder("http", host, 80);
            uriBuilder.Path = "/mscrmservices/2007/crmservice.asmx";

            CrmAuthenticationToken token = new CrmAuthenticationToken();
            token.AuthenticationType = 0;
            token.OrganizationName = orgName;

            service.Url = uriBuilder.ToString();
            service.CrmAuthenticationTokenValue = token;

            service.Credentials = CredentialCache.DefaultCredentials;
            service.UseDefaultCredentials = true;

            return service;
        }
    }
}

プログラムをコンパイルして実行すると例外が発生し、重複データの検出が行われたことが確認できます。 重複ルールの検出は、エンティティが重複データをチェックすることおよび重複データの検出ルールが公開されている必要があるので注意してくだっさい。

取引先企業の重複ルールは、emailaddress1,emailaddress2,emailaddress3 のすべてが他のレコードと一致すると重複とみなされます。Dynamicsの場合、値が null どうしの比較も等しいと判定されるので注意してください。たとえばemailaddress2がnull(ブランク)の場合、比較先のemailaddress2もnullなら一致すると判定されます。

1.1 CreateDuplicatesOptionalParameter と PersistInSyncOptionalParameter について

重複ルールの検出は OptionalParameter のサブクラスである CreateDuplicatesOptionalParameter と PersistInSyncOptionalParameter の設定値が重要なので、次にまとめておきます。

  • CreateDuplicatesOptionalParameter.Valule
    true : 重複データが見つが存在してもデータの登録、更新を行う。(既定)
    false: 重複データが見つかった場合、データの登録、更新は行わない。SoapExceptionが発生
  • PersistInSyncOptionalParameter.Value
    true : マッチコードの更新を同期的に行う。(直後に重複データが登録・更新されても検知可能)
    false: マッチコードの更新を非同期で行う。(直後に重複データが登録・更新されても検知負荷)(既定)

OptionalParameter を指定しないと 既定値(重複データが存在してもデータの登録、更新を行い、マッチコードの更新を非同期で行う) の振る舞いをするので、重複データを登録・更新が行われます。

マッチコードの更新は同期的に行うと、直後に重複データが登録・更新されても重複検出が行えるようになりますがパフォーマンスに影響があるので注意してください。

1.2 マッチコードの更新について

Dynamics は 5分ごとにマッチコードの更新ジョブが実行されます。マッチコードを使用してDynamicsは重複データのチェックを行っているため、非同期にマッチコードを更新する設定でWebサービスを呼び出した場合マッチコードが更新されるまでは重複のデータを投入しても重複データとして検出されません。

同期的にマッチコードを更新すると重複データの検出がすぐに可能になります。

2. 画面で投入したデータとインポートウィザードでの重複データの検出について

画面で投入したデータの重複データの検知が直後で有効にならないのは、マッチコードの更新が非同期で行われるためのようです。

インポートウィザードの場合は、下図のように"重複するレコードをインポートしない"オプションを選択すれば、マッチコードの更新が同期的に行われ、同じインポートファイル内に重複データがあっても重複データとしてを検知してくれるようです。

3.まとめと注意点

説明は以上です。間違い疑問点などありましたらご連絡ください。注意点ですが、マッチコードを同期的に更新したとしても、多重化するなどをして同時に重複ルールに該当するレコードを登録すると登録できてしまいます。マッチコードの更新を同期的に作成すると1秒もかからずに重複が検出されるようになるのですが、ほぼ同時にレコードを作成すると作成できることが確認しました。重複データが1秒以内に作成される場合などに問題になる可能性があります。その場合、非サポートの行為ですがユニークキー制約を設定するなどで対処するひつようがあります。

OptionalParameters は重複データ以外の場所でも使用できます。どのメッセージのときに OptionalParametersが使用できるかは次のリンクを参照してください。

- Using Optional Parameters in Messages
http://msdn.microsoft.com/en-us/library/cc151036.aspx