Dynamics CRM 2011 のアドオンプログラムは、本サイトでもサンプルを紹介している crmsvcutil や OrganizationServiceContext を使用する事前バインディングや、OrganizationServiceProxy を使用する遅延バインディングを使用して.NETで開発することが一般的です。本サイトで紹介しているサンプルは下記リンク参照。

[Dynamics CRM 2011]Early Bound なエンティティクラスを使用したレコードのCRUD操作
[Dynamics CRM 2011]事前バインディング(OrganizationServiceContext)を使用したレコードのCRUD操作
[Dynamics CRM 2011]遅延バインディング(OrganizationServiceProxy)を使用したレコードのCRUD操作   

Dynamics CRM 2011 では、.NET 以外の開発言語(Java等) でもエンティティやレコードを操作するために、 WSDL エンドポイントを使用できます。

今回のサンプルでは、 svcutil を使用して、 WSDL エンドポイント から生成した WCF クライアントプロキシクラスを使用して レコードを操作するサンプルを掲載します。

動作検証環境は次の通り

  • Dynamics CRM 2011 シングルサーバー展開
  • ADFS 2.0 を使用して IFD クレーム認証
  • クライアントは.NET 4.0 のコンソールアプリケーション (.NET 3.5 でも動作確認済)

動作確認を行った環境が、 クレームベース認証を実装した環境だったので、クレームベース認証を行う手順でサンプルの開発の説明をしています。WSDLエンドポイントを使用した場合の AD認証、 ADクレーム認証, IFDクレーム認証, Live認証のサンプルは、SDK 付属のサンプルフォルダ samplecode\cs\wsdlbasedproxies の各プログラムとフォルダ内のreadme.doc, setup.txt が参考になります。

SDKのサンプルでは、 探索サービス(Discovery)用のエンドポイントを使用して 組織のサービス(Organization.svc) 用エンドポイントの URL を求める、より行儀のよいサンプルになっています。今回は WSDLエンドポイントの URL を直接 CRM の画面から調べてサンプルプログラムで使用します。

1. 組織サービス用のWSDL エンドポイントを調べる

WSDL エンドポイントのURL はDynamicsの開発者リソースの画面でアドレスを確認できます。 ナビゲーションメニューの設定をクリックします。メニュー内のカスタマイズを選択します。中央ペインに表示される 開発者リソースをクリックします。下図のようにサービス エンドポイント が表示されます。赤枠で囲んだ 組織のサービスが今回使用する組織のサービスのエンドポイントのアドレスです。AD認証の場合は http://[サーバ名]/XRMServices/2011/Organization.svc がエンドポイントのアドレスになります。WSDLのダウンロードリンクをクリックして直接 WSDL をダウンロードし、svcutilで使用することも可能です。

2. WCFクライアントプロキシクラスの作成

Visual Studio 2010 用のコマンドプロンプトを起動します。svcutil.exe ツールを使用してプロキシクラスを生成します。先程確認した  WSDL エンドポイントのURLに wsdl のクエリ文字列を追加して引数に指定して svcutil を起動します。下例ではプロキシクラスの名前空間を CrmSdk に設定しています。.NET 3.5 用に作成する場合は /targetClientVersion:Version35 オプションを指定します。

警告が出力されますが、output.config の内容は後で編集するので、このまま手順を進めます。

成功すると クライアントのプロキシクラス OrganizationService.cs と エンドポイントのWSDLから生成された構成が設定された output.config が作成されます。

コマンドラインからプロキシを作成しましたが、 Visual Studio からサービス参照の追加で 同様にプロキシクラスを作成できます。

プロキシクラスの生成が完了しました。あとは、 コンソールアプリケーションを作成していきます。

3. サンプルコンソールプログラムの作成

Visual Studio 2010 を起動します。プロジェクトを新規作成し、 コンソールプアプリケーションを作成します。

先ほど追加した WCF クライアントプロキシクラスとコンフィグファイルを追加します。ソリューションエクスプローラから プロジェクトを右クリック→ 追加 → 既存の項目 をクリックします。

svcutil.exe ツールを使用して作成した OrganizationService.cs と output.config をプロジェクトに追加します。output.config は app.config にリネームしてください。ターゲットクライアントが.NET 3.5 の場合はapp.config 内の文字列 decompressionEnabled="true" を削除してください。.NET 3.5 がターゲットクライアントの場合、 decompressionEnabled="true"の設定がないため、実行時に例外が発生します。

次に、必要なアセンブリへの参照を追加します。プロジェクトを右クリック→参照の追加を選択します。 参照の追加ダイアログが表示されます。.NET タブを選択し、 System.ServiceModel と System.Runtime.Serialization を追加します。

app.config のWCFバインディングを編集を行います。app.config をダブルクリックしてエディタで編集します。 system.serviceModel/bindings/customBinding/binding  の子ノード issure ノードをコメントアウトします。

コメントにした issure ノードの下のほうに、 コメントで alternativeIssuedTokenParameters ノードがあるので、 コメント化されたノード内の issure ノードで、 address が http://stsサーバ名/adfs/services/trust/13/usernamemixed となっているノードをコピーします。コピーしたノードを最初にコメントアウトした issure ノードのすぐ下に貼り付けます。サンプルの検証環境では、セキュリティトークンサービスは sts という名前なので、次のようなノードがコピー対象となります。

    <issuer address="https://sts.dcrm.local/adfs/services/trust/13/usernamemixed" bindingConfiguration="https://sts.dcrm.local/adfs/services/trust/13/usernamemixed" binding="ws2007HttpBinding" />

コンフィグレーションの設定は終了です。

続いてプログラムを作成していきます。 既定で作成された Program.cs を次のように編集します。サンプル内の organizationServiceUrl は WSDL用のエンドポイントのURLです。username と password はクレーム認証に使用するユーザのアカウント名とパスワードです。環境によって適宜変更必要があります。サンプルでは、クライアントプロキシの引数でクライアントのエンドポイントの設定名を"CustomBinding_IOrganizationService" としています。もし名前を変更している場合は適宜変更してください。

サンプルでは、 contact (取引先担当者) の作成や更新、 非アクティブ化 を行っています。CRUDや関連付けに関しては、 Create, Update, Retrieve, RetrieveMultiple, Delete, Associate, Disassociate メソッドが使用できます。それ以外のメッセージ(CRMメッセージ)に関しては OrganizationRequest を作成して Execute メソッドを使用する必要があります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;

namespace WsdlIfdSample
{
    class Program
    {
        /// <summary>
        /// 組織WebサービスのURL.本来はDiscoveryServiceから取得する。
        /// DiscoveryServiceから取得するサンプルはSDKのsamplecode\cs\wsdlbasedproxies参照
        /// </summary>
        static string organizationServiceUrl = "https://dyn.dcrm.local/XRMServices/2011/Organization.svc";

        static string username = "testusr";
        static string password = "password";

        static void Main(string[] args)
        {
            // 自己証明書対応用。証明書に問題があっても続行するようにする。
            System.Net.ServicePointManager.ServerCertificateValidationCallback += RemoteCertValidation;

            using (CrmSdk.OrganizationServiceClient client 
                = new CrmSdk.OrganizationServiceClient("CustomBinding_IOrganizationService"
                    , new System.ServiceModel.EndpointAddress(organizationServiceUrl)))
            {
                // IFD クレーム認証
                client.ClientCredentials.UserName.UserName = username;
                client.ClientCredentials.UserName.Password = password;
                // AD 認証で実行ユーザの敷かう情報を使用する場合
                //client.ClientCredentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;
                // AD / ADクレーム認証
                //client.ClientCredentials.Windows.ClientCredential.Domain = "DCRM";
                //client.ClientCredentials.Windows.ClientCredential.UserName = username;
                //client.ClientCredentials.Windows.ClientCredential.Password = password;

                CrmSdk.Entity contact = new CrmSdk.Entity();
                contact.LogicalName = "contact";

                contact.Attributes = new CrmSdk.AttributeCollection();
                contact.Attributes.Add(new KeyValuePair<string, object>("firstname", "ファースト01" + DateTime.Now.ToString()));

                Guid guid = client.Create(contact);
                Console.WriteLine("取引先担当者が作成されました。\tguid:{0}",guid);

                // OrganizationRequest を使用する場合
                CrmSdk.Entity contact2 = new CrmSdk.Entity();
                contact2.LogicalName = "contact";
                contact2.Attributes = new CrmSdk.AttributeCollection();
                contact2.Attributes.Add(new KeyValuePair<string, object>("lastname", "ラスト01" + DateTime.Now.ToString()));
                contact2.Attributes.Add(new KeyValuePair<string, object>("contactid", guid));
                client.Update(contact2);

                CrmSdk.OrganizationRequest request = new CrmSdk.OrganizationRequest();
                // 使用できるリクエスト名は次のURL参照
                // http://msdn.microsoft.com/ja-jp/library/microsoft.xrm.sdk.organizationrequest.requestname.aspx
                request.RequestName = "SetState";
                CrmSdk.EntityReference contactref = new CrmSdk.EntityReference();
                contactref.LogicalName = "contact";
                contactref.Id = guid;
                request.Parameters = new CrmSdk.ParameterCollection();
                CrmSdk.OptionSetValue state = new CrmSdk.OptionSetValue();
                state.Value = 1; // 非アクティブ
                CrmSdk.OptionSetValue status = new CrmSdk.OptionSetValue();
                status.Value = -1;
                request.Parameters.Add(new KeyValuePair<string, object>("EntityMoniker", contactref));
                request.Parameters.Add(new KeyValuePair<string,object>("State", state));
                request.Parameters.Add(new KeyValuePair<string,object>("Status", status));

                client.Execute(request);

            }

        }

        static bool RemoteCertValidation(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
        {
            if (error != SslPolicyErrors.None)
            {
                Console.WriteLine("証明書に問題があります。理由:" + chain.ChainStatus[0].StatusInformation);
                Console.Write("処理を続行しますか? y:続行:");
                string input = Console.ReadLine();
                if (input.ToLower() == "y")
                    return true;
                else
                    return false;
            }
            return true;
        }
    }
}

OrganizationRequest で 使用可能なメッセージに関しては、次のURLを参照してください。

OrganizationRequest.RequestName Property
http://msdn.microsoft.com/ja-jp/library/microsoft.xrm.sdk.organizationrequest.requestname.aspx

以上で終了ですといいたいのですが、本サンプルを実行すると Main メソッドの最後の client.Execute で例外が発生します。下図を参照してください。既知の型に指定されていないため、シリアル化に失敗した旨のエラーメッセージが表示されています。

上記で詳細の表示をクリックし、InnerException のMessage を確認すると静的に認知されていないすべての型を既知の型の一覧に追加してください というメッセージが表示されます。

エラー内容に従って、 OrganizationService.cs の OrganizationRequest クラスに シリアル化でエラーとなった型を引数にとるKnownTypeAttribute を次の赤字のように指定します。

    [System.Runtime.Serialization.KnownTypeAttribute(typeof(OptionSetValue))]
    [System.Runtime.Serialization.KnownTypeAttribute(typeof(EntityReference))]

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute(&quot;System.Runtime.Serialization&quot;, &quot;4.0.0.0&quot;)]
    [System.Runtime.Serialization.DataContractAttribute(Name=&quot;OrganizationRequest&quot;, Namespace=&quot;http://schemas.microsoft.com/xrm/2011/Contracts&quot;)]
    public partial class OrganizationRequest : object, System.Runtime.Serialization.IExtensibleDataObject

サンプルで使用していませんが、 CreateRequest 等のメッセージを使用する場合は、 Entity も KnownTypeAttribute に指定して アノテートする必要があります。

以上でサンプルプログラムが動作するようになりました。

4. まとめ

説明は以上です。今回はIFDクレーム認証を前提としてサンプルを作成する手順を記載しましたが、各認証方式(AD,Online, IFD, AD Claim )に対応したWSDL エンドポイントを使用したサンプルはDynamics CRM 2011 SDKのに付属する sdk\samplecode\cs\wsdlbasedproxies フォルダのプログラムおよび説明が参考になります。

.NET する場合、 svcutil ではなく、 crmsvcutil を使用した事前バインドクラスや sdkの dll に含まれる OrganizationServiceProxy を使用し 遅延バインドを使用して開発を行うほうが簡単かつ効率が良いと思います。事前バインドの場合は、生成されるプロキシクラスによってタイプセーフ、インテリセンスのサポートを得られます。それにもかかわらず、一般的な svcutil を使用した 軽量のWCFプロキシクライアントを使用したプログラミングの方法を検証しました。

理由は、 SSIS のスクリプトタスクを使用して CRM 2011 のレコードを操作する方法を調べたかったためです。SQL Server 2008 R2 の SSIS は .NET 3.5 を前提としているので.NET 4.0 用のアセンブリが参照できないという問題があります。後方互換用のエンドポイントを使用すれば開発は行えますが、それ以外の方法がないものかと思い一般的なsvcutilで作成できる一般的なWSDLエンドポイントを使用するWCFクライアントを使用した開発方法を調べてみました。