Advanced Developer Extensions for Dynamics CRM を使用してエンティティレコードを作ったり、更新したり、削除したり、参照したりしてみます。といっても Dynamics CRM SDK に付属する次のドキュメントや、サンプルプログラム(sdk\microsoft.xrm\samples\consoleフォルダ)から確認することもできます。

  • advanced_developer_extensions_-_developers_guide.docx
  • advanced_developer_extensions_-_walkthrough_consoleapp.docx
  • microsoftxrm.chm

今回は、AD認証(統合認証)環境でサンプルプログラムの動作確認をしています。.NET のバージョンは 3.5 SP1 です。

1. 環境準備

Visual Studio 2008 を起動して、コンソールプロジェクトを作成します。プロジェクトのターゲットは.NET 3.5 です。

1.1 DataContext, エンティティクラスの自動生成

統合認証を使用して次のコマンドで単一ファイルにコードの自動生成を行いました。

crmsvcutil.exe を使用して統合環境で次のコマンドを使用してクラスを自動生成します。クラスの名前空間はEntitiesになります。

crmsvcutil.exe /server:http://localhost/dyn /out:Xrm.cs

作成したプログラムを Visual Studio のコンソールプロジェクトに追加します。

 

1.2 dllの参照の追加

プロジェクトの参照の追加で次の .NET dll を追加します。

  • System.Data.Services
  • System.Data.Services.Client

同じくファイル参照の追加で、Dynamics CRM SDK に付属する次の dll への参照を追加します。コンソールアプリの場合、antixsslibrary.dllは参照に追加しなくてもOKのはずです。がxrm関連のdllを参照に追加すると、antixsslibrary.dllもプロジェクトのbinフォルダにコピーされるようなので、明示的に追加しておきます。(06 13 2009 追記 : microsoft.xrm.portal.*.dllもコンソールの場合は必要ないみたいです。)

  • microsoft.crm.sdk.dll
  • microsoft.crm.sdk.typeproxy.dll
  • microsoft.xrm.client.dll
  • microsoft.xrm.portal.dll
  • microsoft.xrm.portal.files.dll
  • antixsslibrary.dll

この時点でいったんビルドを実施し、エラーが発生しないことを確認します。

エンティティ名とそのエンティティの属性名が同じだとビルドに失敗します。その場合は属性名を変えればとりあえずビルドは通ります。副作用あるんだろうか?

1.3 接続文字列の追加

アプリケーション構成ファイルをプログラムに追加し、connectionStrings セクションに次の記述を追加します。

<connectionStrings> 
  <add name="XRM" connectionString="AuthenticationType=
    Integrated; Server=http://localhost/dyn"/> 
 </connectionStrings>

接続文字列は 認証方法に従って適切に設定する必要があります。接続文字列については[Dynamics CRM] Dynamics CRM 4.0 Advanced Developer Extensions を使ってみる コード生成編 や SDK に付属するadvanced_developer_extensions_-_developers_guide.docx を参照してください。参照して下さい。

さて、 とりあえず準備OK、いろいろやってみます。

2. Advanced Developer Extension を使っていろいろする

2.1 検索してみる

とりあえず、取引先企業を検索してみます。RetrieveAccount メソッドが処理のメインです。 CrmDataContext を継承して自動生成された、DatContext に接続文字列のキーを指定して取引先企業の一覧を取得を行います。LINQクエリを使用して詳細な検索条件を指定することもできます。メソッドに示す通り、エンティティのフィールドを介して型付けされた属性にも簡単にアクセスすることができます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Entities;
using Microsoft.Crm.Sdk;

namespace Xrm.Sample.CUI
{
    class Program
    {
        static void Main(string[] args)
        {
            RetrieveAccount();
            //CreateRetrieveUpdateDelete();
            //SetAddDeleteLink();
            //DynamicsCreateRetrieveUpdateDelete();
            //DynamicSetAddDeleteLink();
            Console.WriteLine("Please Enter to exit..");
            Console.Read();
        }

        static void RetrieveAccount()
        {
            // とりあえず企業担当者一覧をゲッツしてみる
            var xrm = new DataContext("XRM");
            var accounts = xrm.accounts;

            foreach (var a in accounts)
            {
                Console.WriteLine("■company name:{0}", a.name);

                // 1:Nが同一エンティティの関連の場合、スキーマ名が接尾辞に追加されるみたい
                var parent_account = a.account_parent_account_account;
                Console.WriteLine(" parent company");
                if (parent_account != null)
                {
                    Console.WriteLine("\tparent name:{0}", parent_account.name);
                }
                Console.WriteLine(" subsidiary companies");
                var subsidiary_account = a.account_parent_account;
                foreach (var s in subsidiary_account)
                {
                    Console.WriteLine("\tsubsidiary company name:{0}", s.name);
                }
            }
        }

    }
}

2.2 Retrieve したり、Create したり、Update したり、 Deleteしたりしてみる

エンティティレコードの検索や作成や更新、削除を行います。エンティティの新規追加は CrmDataContext を継承して作成されたDataContextクラスのAddToエンティティ名 メソッドを使用します。更新の場合はUpdateObject, 削除の場合は、DeleteObject を使用します。新規作成ではないが、DataContextに追加する場合は AttachTo メソッドを使用します。

変更の確定を行う場合は、 DataContext の SaveChanges メソッドを呼び出します。Lookup属性は、lookup属性列に直接GUIDを設定して設定したり、関連名の属性にエンティティを設定して参照を設定することもできます。後述するSetLink,AddLinkを使用することも可能です。

/// <summary>
/// 検索したり、作成したり、更新したり、削除するサンプル
/// 変更は Savechangesを呼び出すまで保存されないので注意.
/// </summary>
static void CreateRetrieveUpdateDelete()
{
    var xrm = new DataContext("XRM");
    var parent = (from a in xrm.accounts
                  where a.name == "Company 3" && a.statecode == "Active"
                  select a).First();
    // 作成
     var account = new Entities.account()
    {
        name = "Company ABC",
        revenue = 1000m,
        account_parent_account_account = parent
    };
    xrm.AddToaccounts(account);
    xrm.SaveChanges();

    var subsidirary = new Entities.account()
    {
        name = "Company DDD",
        revenue = 200m,
        parentaccountid = account.accountid,
        description = "subsidiary company of Company ABC"
    };
    xrm.AddToaccounts(subsidirary);

    // 更新
     account.numberofemployees = 1000;
    account.emailaddress1 = "test@example.jp";
    xrm.UpdateObject(account);
    xrm.SaveChanges();

    // 削除
     xrm.DeleteObject(account);
    xrm.SaveChanges();
}

2.3 1対N, N対1, N対N 関連属性を設定する

1:NやN:1, N:N 関連の属性を設定、追加、削除を行う場合 DataContext の SetLink(N:1,1:Nの場合), AddLink, DeleteLink (N:Nの場合)を使用します。SetLink,AddLink,DeleteLinkの引数の文字には関連名を指定します。

N:1 の場合は、 Lookup属性や関連属性に直接値を設定することもできます。この場合はSaveChanges の前に、UpdateObject を呼び明日必要があります。

/// <summary>
/// 関連をセットしたり、追加したり、削除するサンプル
/// N対1の場合
/// - SetLink を使用する
/// - Lookup属性に参照先のGUIDを設定して SaveChanges を使用する
///  (関連付け属性に参照先エンティティのインスタンスを設定するでも可)
/// </summary>
static void SetAddDeleteLink()
{
    var xrm = new DataContext("XRM");
    var parent = (from a in xrm.accounts
                   where a.name == "Company 3" && a.statecode == "Active"
                  select a).First();
    #region SetLink を使用する
     var contact1 = (from c in xrm.GetEntities<contact>()
                    where c.firstname == "X" && c.lastname == "Agent"
                    select c).FirstOrDefault();

    if (contact1 == null)
    {
        contact1 = new Entities.contact();
        contact1.firstname = "X";
        contact1.lastname = "Agent";
        xrm.AddTocontacts(contact1);
        xrm.SaveChanges();
    }
    xrm.SetLink(contact1, "contact_customer_accounts", parent); // 関連付けを解除する場合はnullセット
     xrm.SaveChanges();
    #endregion

    #region 関連付け属性に参照先エンティティのインスタンスを設定する
     var contact2 = (from c in xrm.GetEntities<contact>()
                    where c.firstname == "Y" && c.lastname == "Agent"
                    select c).FirstOrDefault();

    if (contact2 == null)
    {
        contact2 = new Entities.contact();
        contact2.firstname = "Y";
        contact2.lastname = "Agent";
        xrm.AddTocontacts(contact2);

        xrm.SaveChanges();
    }
    contact2.contact_customer_accounts = parent; // 関連付けを解除する場合はnullセット
     xrm.UpdateObject(contact2);                  // UpdateObjectを忘れずに
     xrm.SaveChanges();
    #endregion

    #region Add-Link, Delete-Link を使用する(N:1,N:Nで使用可能)
    var contact3 = (from c in xrm.GetEntities<contact>()
                    where c.firstname == "Z" && c.lastname == "Agent"
                    select c).FirstOrDefault();

    if (contact3 == null)
    {
        contact3 = new Entities.contact();
        contact3.firstname = "Z";
        contact3.lastname = "Agent";
        xrm.AddTocontacts(contact3);

        xrm.SaveChanges();
    }

    if (contact3.contact_customer_accounts == null)
    {
        xrm.AddLink(parent, "contact_customer_accounts", contact3);
    }
    xrm.SaveChanges();

    //N:N の AddLink, DeleteLink サンプル(標準のもので適当なものがみつからなかったので自作)
    //var divs = xrm.new_account_divs.ToList();
    //var user = xrm.systemusers.Where(x => x.firstname.StartsWith("A")).First();
    //foreach (var div in divs)
    //{
    //    xrm.AddLink(user, "new_account_div_systemuser", div);
    //}
    //xrm.SaveChanges();
    //foreach (var div in divs)
    //{
    //    xrm.DeleteLink(user, "new_account_div_systemuser", div);
    //}
    //xrm.SaveChanges();            
    #endregion

    #region 関連付け属性に対応するLookup属性にGUIDを設定する
     parent.primarycontactid = contact3.contactid;
    xrm.UpdateObject(parent);
    xrm.SaveChanges();
    #endregion
}

2.4 動的型付で検索したり、作成したり、更新したり、削除したりする

動的意に片付される場合でエンティティレコードを作成したり、更新したり、削除したり検索してみます。データの検索は GetEntities メソッドを使用して検索条件を構築します。プロパティ属性値は GetProperty, SetProperty メソッドを使用して値を取得します。Lookup 属性、N対1関連にはSetProperty メソッドでGUIDをセットするか、 SetRelatedEntity メソッドを使用して、参照先のエンティティを設定ます。

SetProperty メソッドはCrmタイプを指定するオーバーロードバージョンが存在します。.NETの型と自動的に変換されるCrmタイプおよび自動的に変換されるタイプのSDKのドキュメントから転載します。

自動的に変換される型

オリジナルの型(.NET型) 変換後の型(Crm型)
System.Boolean (bool) Microsoft.Crm.Sdk.CrmBoolean
System.DateTime Microsoft.Crm.Sdk.CrmDateTime
System.Decimal (decimal) Microsoft.Crm.Sdk.CrmDecimal
System.Single (float) Microsoft.Crm.Sdk.CrmFloat
System.Double (double) Microsoft.Crm.Sdk.CrmDouble
System.Int32 (int) Microsoft.Crm.Sdk.CrmNumber
System.Guid Microsoft.Crm.Sdk.Key

SetPropertyのCrmタイプを指定するオーバーロード版を呼び出す必要のある型

オリジナルの型(Crm型) 変換する型(.NET型)
Microsoft.Crm.Sdk.Picklist System.Int32
Microsoft.Crm.Sdk.CrmMoney System.Decimal
Microsoft.Crm.Sdk.EntityNameReference System.String
Microsoft.Crm.Sdk.Status System.Int32
Microsoft.Crm.Sdk.UniqueIdentifier System.Guid
Microsoft.Crm.Sdk.Lookup System.Guid and an entity name
Microsoft.Crm.Sdk.Customer System.Guid and an entity name
Microsoft.Crm.Sdk.Owner System.Guid and an entity name

変更を確定するには 静的型付と同様 CrmDataContextのSaveChanges メソッドを呼び出します。

/// <summary>
/// 動的に検索したり、作成したり、更新したり、削除ったり
/// SaveChangesを呼ぶまで反映されないので注意
/// </summary>
static void DynamicsCreateRetrieveUpdateDelete()
{
    var xrm = new DataContext("XRM");
    var parent = (from a in xrm.GetEntities("account")
                  where a.GetPropertyValue<string>("name") == "Company 3"
                    && a.GetPropertyValue<string>("statecode") == "Active"
                  select a).Single();
    // 作成
    var account = xrm.CreateEntity("account");
    account.SetPropertyValue("name", "Company XYZ");
    account.SetPropertyValue("revenue", 1000m, typeof(CrmMoney));
    account.SetRelatedEntity("parentaccountid", parent);
    // 関連名を指定してもOK
    //account.SetRelatedEntity("account_parent_account", parent);
    xrm.AddObject("account", account);

    xrm.SaveChanges();

    var subsidiary = xrm.CreateEntity("account");
    subsidiary.SetPropertyValue("name", "Company ZZZ");
    subsidiary.SetPropertyValue("revenue", 250m, typeof(CrmMoney));
    subsidiary.SetPropertyValue("parentaccountid", account.Id.Value, typeof(Lookup), "account");
    subsidiary.SetPropertyValue("description", "subsidiary company of Company XYZ");
    xrm.AddObject("account", subsidiary);

    xrm.SaveChanges();

    // 更新
     account.SetPropertyValue("numberofemployees", 1000);
    account.SetPropertyValue("emailaddress1", "test@example.jp");
    xrm.UpdateObject(account);
    xrm.SaveChanges();

    // 削除
    xrm.DeleteObject(account);
    xrm.SaveChanges();
}

2.5 動的型付で関連を設定したり、追加したり削除したりする

動的片付でも、関連の設定には、 SetLink, AddLink, DeleteLink を使用します。前述サンプルのようにN:1の場合は SetRelatedEntity を使用したり、Lookup属性に直接参照先のGUIDを設定することもできます。

/// <summary>
/// 動的に関連をセットしたり、追加したり、削除するサンプル
/// N対1 の場合は
/// - XrmDataContext.SetLink を使用する
/// - Lookup属性に参照先のGUIDを設定して SaveChanges を呼び出す
/// - SetRelatedEntity を使用する
/// 
/// 1対N,N対Nの場合はAddLink,DeleteLinkを使用する
/// </summary>
static void DynamicSetAddDeleteLink()
{
    var xrm = new DataContext("XRM");
    var parent = (from a in xrm.GetEntities("account")
                  where a.GetPropertyValue<string>("name") == "Company 3"
                  && a.GetPropertyValue<string>("statecode") == "Active"
                  select a).Single();

    #region SetLink を使用する
    var contact1 = (from c in xrm.GetEntities("contact")
                    where c.GetPropertyValue<string>("firstname") == "O" && c.GetPropertyValue<string>("lastname") == "Agent"
                    select c).FirstOrDefault();

    if (contact1 == null)
    {
        contact1 = xrm.CreateEntity("contact");
        contact1.SetPropertyValue("firstname", "O");
        contact1.SetPropertyValue("lastname", "Agent");
        xrm.AddObject("contact", contact1);

        xrm.SaveChanges();
    }
    // xrm.SetLink(contact1, "parentaccountid", parent); // こっちOK
    xrm.SetLink(contact1, "contact_customer_accounts", parent); // 関連付けを解除する場合はnullセット
     xrm.SaveChanges();
    #endregion


    #region Add-Link, Delete-Link を使用する(N:1,N:Nで使用可能)
    var contact3 = (from c in xrm.GetEntities("contact")
                    where c.GetPropertyValue<string>("firstname") == "Q" 
                    && c.GetPropertyValue<string>("lastname") == "Agent"
                   select c).FirstOrDefault();

    if (contact3 == null)
    {
        contact3 = xrm.CreateEntity("contact");
        contact3.SetPropertyValue("firstname", "Q");
        contact3.SetPropertyValue("lastname", "Agent");
        xrm.AddObject("contact", contact3);

        xrm.SaveChanges();
    }
    //var parentaccount = contact3.GetRelatedEntity("contact_customer_accounts");
    //if (parentaccount != null)
    //{
    //    xrm.DeleteLink(parentaccount, "parentaccountid", contact3);
    //}
    //else
    //{
    //    xrm.AddLink(parent, "contact_customer_accounts", contact3);
    //}
    xrm.UpdateObject(contact3);
    xrm.SaveChanges();

    #endregion

}

2.6 LINQ の利用

生成されたCrmDataContext やエンティティクラスに対して LINQ 式を使用することができます。

Microsoft Dyamics CRM Providerで実装されている機能です

  • Join, Where, OrederBy, Select メソッド
  • Skip,Take,Distinct,Single,Firstメソッド
  • IEnumerable 拡張の処理
  • 匿名型のSelectのサポート
  • Where条件での文字列型に対する Contains, StartsWith, EndsWith, Equalsメソッド
  • 静的、動的な属性値の使用

ちなみに、CrmDataContext の Translate メソッドを使用して引数に LINQ式を渡すと、QueryExpression に変換できます。

LINQ式はここまでの説明でいくつか使用しています。

3. まとめ

説明は以上です。Advanced Developer Extensions を使用することで、 CrmService に特化したAPIの使い方を理解する必要なしに、LINQ式やEntityFramework, LINQ to SQL と同じプログラミングモデルでアプリケーションを開発できるようになります。

とはいっても、内部的には Advanced Developer Extensions は CrmService, MetadataService を使用していますが。自動生成されるDataContext は WCF Data Service (ADO.NET Data Service ) でも使用することができます。設定サンプルは、SDK付属ドキュメントadvanced_developer_extensions_-_walkthrough_webapp.docxやサンプルを参照して下さい。