October 2011

Volume 26 Number 10

Silverlight - Silverlight 4 から Dynamics CRM 4.0 API の能力を利用する

Mark Beckner | October 2011

Microsoft Dynamics CRM 4.0 ソリューションを実装する企業が増えています。既存の Web サービス ベースの API と統合できる外部アプリケーションを構築する必要性を感じている企業も増えています。

Microsoft Dynamics CRM 4.0 (以下、CRM 4.0) を直接操作する Silverlight アプリケーションを構築するのが難しいことはわかっています。これは、Silverlight の呼び出しが非同期に行われる性質があることと、CRM 4.0 Web サービスを直接呼び出せないことに起因しています。今回の記事では、CRM 4.0 Web サービス API 経由でデータの読み書きを行える Silverlight アプリケーションを構築する方法について詳しく説明します。

ソリューションの概要

Silverlight 4 と CRM 4.0 は、どちらも強力なテクノロジですが、この 2 つのテクノロジの統合は容易ではありません。ここでは、Silverlight と CRM 4.0 Web サービス API との間の非同期通信を調査しながら、Silverlight と CRM 4.0 の統合について掘り下げます。

一般に、Silverlight 以外のアプリケーションでは、Web サービスへの呼び出しを同期をとって行います。つまり、呼び出しを行うと、その応答を受け取るまでアプリケーションが待機します。この間、ユーザーはアプリケーションを操作できず、ラウンドトリップが完全に終わるまで待機しなければなりません。Silverlight などの非同期アプリケーションの場合は、サービスへの呼び出しを行った後、応答が返る前でもアプリケーションは完全に機能を続けます。これにより、リッチで動的なユーザー エクスペリエンスが実現されます。しかし、開発者の負担は大きくなります。

今回は、この通信のしくみを理解するために、いくつかの機能を示します。まず、Silverlight アプリケーションをセットアップする方法を示し、CRM 4.0 API のラッパーとして動作する Web サービスとの通信に必要な手順を説明します。次に、CRM 4.0 API の操作を詳しく解説し、Web サービスのラッパーを使用してデータの読み書きを行う方法を示します。さらに、CRM 4.0 のコア システム ユーザー エンティティの操作や、動的エンティティの操作について取り上げます。最後に、Silverlight に返される結果セットを扱う方法を示します。この記事を読み終えるころには、Silverlight と CRM 4.0 の独自の統合を簡単に構築できるようになるでしょう。

Silverlight 4 アプリケーションを作成する

CRM 4.0 を操作するよう Silverlight アプリケーションを構成するには、多くの手順を踏まなければなりません。Silverlight プロジェクトから CRM 4.0 SDK または Web サービス API を直接参照することは可能ですが、実際には、ほとんどのインターフェイスとメソッドはコードから呼び出すことができません。CRM 4.0 API を操作するには、ラッパー Web サービスを作成する必要があります。この Web サービスは、Silverlight アプリケーションと CRM 4.0 API の間の呼び出しを、Silverlight が処理できる形式で仲介します。このラッパー Web サービスは、SilverlightCRMDemo.Web アプリケーションに直接追加できます。

この手順を開始するには、まず Visual Studio 2010 で CRM40SilverlightDemo という新しい Silverlight ソリューションを作成します。Visual Studio で Silverlight アプリケーションを作成すると、中核となる Silverlight アプリケーションと、この Silverlight アプリケーションを Web ページに埋め込む ASP.NET アプリケーションの 2 つのプロジェクトが常に作成されます。この ASP.NET アプリケーションは、CRM 4.0 API を操作することになる Web サービス ラッパーもホストします。Silverlight アプリケーションは、サービス参照を使ってこの Web サービスを参照します。

Web サービス ラッパーを作成するには、ASP.NET アプリケーションに CrmServiceWrapper という新しい Web サービスを追加します。今回の例では、このサービスに、CRM データを取得する Web メソッドと、CRM データをポストする Web メソッドを追加します。図 1 は、スタブとして作成した現時点のこれらのメソッドを示しています。Silverlight アプリケーションがこのラッパー サービスとの通信に成功すれば、これらのメソッドを更新して、実際の CRM 4.0 API を呼び出すことになります。

図 1 Web メソッド スタブ

public class CrmServiceWrapper : System.Web.Services.WebService
{
  [WebMethod]
  public string GetCRMData()
  {
    return "This is the stubbed return for retrieving";
  }
 
  [WebMethod]
  public string PostCRMData()
  {
    return "This is the stubbed return for posting data";
  }
}

Web サービス ラッパーを ASP.NET アプリケーションに追加したら、デバッグ モードでこの ASP.NET アプリケーションを実行し、デバッガーがアプリケーションを実行している場所の URL を取得する (表示されるブラウザー ウィンドウから簡単に URL を取得できます) ことで、Silverlight アプリケーションから ASP.NET アプリケーションへの参照を最も簡単に追加できます。URL を取得したら、Silverlight アプリケーションに CrmServiceReference という新しいサービス参照を追加して、その URL を貼り付けます。関連する構成ファイルとコードがすべて自動的に修正されます。これ以外の方法では、アプリケーションを適切にデバッグするために、ドメイン間参照の例外と、かなり複雑なセットアップに対処する必要があります。

これで参照を追加したので、Silverlight アプリケーションでの実際のコーディングが実行できるようになります。コードでは、Web メソッドごとにイベント ハンドラーを定義して、それらの Web メソッドへの呼び出し完了後にデータを処理する 2 つのメソッドを作成します。図 2 のコードを、Silverlight アプリケーションの MainPage.xaml.cs ファイルに直接追加できます。これを実行すると、両方のメソッドが同時に実行されるようになります。

図 2 MainPage.xaml.cs へのコードの追加

using CRM40SilverlightDemo.CrmServiceReference;
 
public partial class MainPage : UserControl
{
  public MainPage()
  {
    InitializeComponent();
 
    // Call the GetCRMData Web method.
    CrmServiceWrapperSoapClient proxyGet =
      new CrmServiceWrapperSoapClient();
    proxyGet.GetCRMDataCompleted +=
      new EventHandler<GetCRMDataCompletedEventArgs>(proxy_GetCRMDataCompleted);
    proxyGet.GetCRMDataAsync();
 
    // Call the PostCRMData Web method.
    CrmServiceWrapperSoapClient proxyPost = new CrmServiceWrapperSoapClient();
    proxyPost.PostCRMDataCompleted +=
      new EventHandler<PostCRMDataCompletedEventArgs>(proxy_PostCRMDataCompleted);
    proxyPost.PostCRMDataAsync();
  }
 
  // Called asynchronously when the GetCRMData Web method returns data.
  void proxy_GetCRMDataCompleted(object sender, GetCRMDataCompletedEventArgs e)
  {
    // Do something with the data returned.
    string result = e.Result.ToString();
  }
 
  // Called asynchronously when the PostCRMData Web method returns data.
  void proxy_PostCRMDataCompleted(object sender, PostCRMDataCompletedEventArgs e)
  {
    // Do something with the data returned.
    string result = e.Result.ToString();
  }
}

Silverlight アプリケーションがエラーなしで実行され、Web サービスからデータが返されることを確認したら、今度は CRM 4.0 API への呼び出しを作成します。これらの呼び出しはすべて、既に作成済みの Web サービスラッパー、GetCRMData メソッドおよび PostCRMData Web メソッドに含めます。

CRM 4.0 API を操作する

CRM 4.0 からは主要な Web サービスとして CRMService と MetadataService を使用できます。これらの Web サービスは、通常、すべてのプロジェクトから参照できます (ただし、Silverlight アプリケーションから参照する場合、実際にはあまり多くの機能を使えません)。API を操作する最も一般的で効率的な方法が Microsoft Dynamics CRM SDK で、bit.ly/6M3PvV (英語) からダウンロードできます。この SDK には、膨大な数のクラスとメソッドが含まれており、.NET Framework コードと CRM 4.0 Web サービスとの通信を簡略化します。ここでは、Web サービス ラッパーからこの SDK を使用して API を操作する方法を習得します。

適切な CRM SDK アセンブリを参照することから始めます。Web サービス ラッパーをホストする ASP.NET アプリケーションで、microsoft.crm.sdk.dll と microsoft.crm.sdktypeproxy.dll という 2 つの SDK アセンブリへの参照を追加します。参照を追加したら、以下に示す適切なディレクティブを CrmServiceWrapper.asmx ページの先頭に追加します。

using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;

次に、CRM サービスのインスタンスを作成するコードを記述します。これで、CRM 4.0 インスタンスに接続できるようになります。このコードは、GetCRMData と PostCRMData の両方の Web メソッドから使用するため、独立したメソッドとして用意します。このメソッド (図 3 参照) には、2 つの重要なフィールドが必要です。1 つは、CRM 4.0 インスタンスの組織名、もう 1 つは (/MSCRMServices/2007/crmservice.asmx に置く) メイン CRM サービスの URL です。これらのフィールドは、コードのコンパイル後に簡単に変更できるようにするため、構成ファイルで管理するのが最適です。

図 3 GetCRMService メソッド

static public CrmService GetCRMService()
{
  CrmService service = new CrmService();
  CrmAuthenticationToken token =
    new Microsoft.Crm.Sdk.CrmAuthenticationToken();
  token.OrganizationName = "Contoso";
  service.Url = "http://localhost:5555/MSCRMServices/2007/crmservice.asmx";
  service.Credentials = System.Net.CredentialCache.DefaultCredentials;
  service.CrmAuthenticationTokenValue = token;
  return service;
}

CRM データを照会する

今度は、CRM 4.0 からデータを照会します。重要な点は、CRM 4.0 には、コア システム エンティティとカスタム エンティティの 2 種類のエンティティがあることです。コア システム エンティティは、カスタム エンティティよりも操作がやや簡単です。既定では、C# の厳密に型指定されたオブジェクトを通じて、コア エンティティのすべてのプロパティを取得できます。カスタム エンティティは、一般に、動的エンティティとして照会しますが、厳密に型指定されたオブジェクトとして扱うことも可能です。ここでは、両方のエンティティを使ってデータを照会する方法を示します。

図 4 の例では、コア システム エンティティ (この例では、System User (システム ユーザー) エンティティ) を照会しています。このコードは、先ほどスタブとして作成した同じ名前の Web メソッドと置き換えます。CRM 4.0 にすべてのシステム ユーザーを照会するこの新しいコードには、いくつか重要な点があります。1 つは、扱うオブジェクトの型が "systemuser" である点です。すべてのコア エンティティには、固有の型があります。もう 1 つは、返される結果が XML ドキュメントの文字列表記である点です。

図 4 システム ユーザー エンティティの照会

[WebMethod]
public string GetCRMData()
{
  // This will return all users in CRM in a single XML structure.
  StringBuilder xml = new StringBuilder();
  CrmService service = GetCRMService();
 
  QueryExpression query = new QueryExpression();
  query.EntityName = "systemuser";
  query.ColumnSet = new AllColumns();
 
  RetrieveMultipleRequest retrieve = new RetrieveMultipleRequest();
  retrieve.Query = query;
  retrieve.ReturnDynamicEntities = false;
 
  RetrieveMultipleResponse retrieved =
    (RetrieveMultipleResponse)service.Execute(retrieve);
 
  xml.Append("<Users>");
 
  for (int i = 0; i <
    retrieved.BusinessEntityCollection.BusinessEntities.Count; i++)
  {
    systemuser user =
      (systemuser)retrieved.BusinessEntityCollection.BusinessEntities[i];
 
    // Create a string represenation to return to Silverlight app.
    xml.Append("<User");
    xml.Append(" FirstName ='" + user.firstname + "'");
    xml.Append(" LastName = '" + user.lastname + "'");
    xml.Append(" SystemUserId = '" + user.systemuserid.ToString() + "'");
    xml.Append(" JobTitle = '" + user.jobtitle + "'");
    xml.Append("/>");
  }
 
  xml.Append("</Users>");
 
  return xml.ToString();
}

Silverlight にデータを返す選択肢がかなり限られているように感じられるかもしれません。たとえば、Silverlight から直接 API を操作できないため、操作対象の BusinessEntityCollection を返すことができません。さらに、Web サービスを経由して Silverlight に XML を渡すのにも制限があります。このため、結局は、シンプルな文字列を扱うのが最適だと言えます。

カスタム エンティティの照会はもう少し複雑です。データを取得する最も一般的な方法は、動的エンティティを使用して結果を取得することです (この取得法の例を図 5 に示します)。このアプローチの課題は、クエリ式内でフィルターの特定の属性を扱うところにあります。不可能なことは何もありませんが、IntelliSense があまり役立ちません。

図 5 動的エンティティを使用した取得

public static DynamicEntity GetCRMEntity(
  CrmService tmpService, String entityId, String entityName)
{
  DynamicEntity crmEntity = null;
 
  TargetRetrieveDynamic targetRetrieve = new TargetRetrieveDynamic();
 
  // Set the properties of the target.
  targetRetrieve.EntityName = entityName;
  targetRetrieve.EntityId = new Guid(entityId);
 
  // Create the request object.
  RetrieveRequest retrieve = new RetrieveRequest();
 
  // Set the properties of the request object.
  retrieve.Target = targetRetrieve;
  retrieve.ColumnSet = new AllColumns();
 
  // Retrieve as a DynamicEntity.
  retrieve.ReturnDynamicEntities = true;
 
  // Execute the request.
  RetrieveResponse retrieved = (RetrieveResponse)tmpService.Execute(retrieve);
 
  // Extract the DynamicEntity from the request.
  DynamicEntity entity = (DynamicEntity)retrieved.BusinessEntity;
 
  crmEntity = entity;
 
  return crmEntity;
}

動的エンティティのアプローチが最も一般的ですが、Visual Studio でエンティティの構造をすべて確認して、標準エンティティと同じ方法で操作する場合は、CrmService へのプロキシを作成します。多くの場合、これにより、開発エクスペリエンスが劇的に向上し、コードの記述方法がより柔軟になります。プロキシは、CrmService WSDL の最新のインスタンスを基盤とする生成済みの C# ファイルにすぎません。メイン CrmService サービスのプロキシ クラスを作成するには、Visual Studio コマンド プロンプトを開いて、次のように入力します。URL は、crmservice.asmx ページへの適切なリンクに置き換えます。

wsdl.exe /out:CrmSdk.cs /namespace:CRM40SilverlightDemo.WebReferences.CrmSdkhttp://localhost:5555/mscrmservices/2007/crmservice.asmx?wsdl

このコマンドは、wsdl.exe 実行可能ファイルを実行したディレクトリに、CrmSdk.cs という C# ファイルを作成します。このファイルをプロジェクトに追加します。追加後は、コア システム エンティティとまったく同じ方法でカスタム エンティティを操作します。エンティティを変える場合、プロキシの C# ファイルを更新するだけで、新しい属性 (または、その他の変更内容) が利用できるようになります。今回は、演習を目的として、プロキシ C# ファイルは使用しません。

CRM 4.0 データを更新する

CRM 4.0 からデータを取得する方法について見てきたので、今度はデータのポストについて説明します。図 6 は、システム ユーザー レコードを更新する方法を示しています。このためには、CRM 4.0 レコードの一意識別子とニックネームの 2 つのプロパティを渡す必要があります。これらの文字列を渡すには、次に示す MainPage.xaml.cs のコードを 1 行変更します。

proxyPost.PostCRMDataAsync("f04b02d9-ad5f-e011-a513-000c29330bd5","My Nickname");

ID は、PostCRMData メソッドへの呼び出しでハードコードしている点に注意してください。ID を動的に取得するメカニズムについては読者の皆さんの課題とします。

 

図 6 CRM 4.0 データのポスト

[WebMethod]
public string PostCRMData(string userId, string nickname)
{
  CrmService service = GetCRMService();
 
  Key kid = new Key();
  kid.Value = new Guid(userId);
  systemuser entity = new systemuser();
  entity.systemuserid = kid;
 
  entity.nickname = nickname;
 
  service.Update(entity);
 
  return "success";
}

Silverlight で結果を処理する

この時点でのソリューションは、CRM 4.0 との間でデータの取得とポストが可能になっています。しかし、Silverlight に返された文字列の結果に対しては何も行っていません。GetCRMData メソッドは、すべてのユーザー レコードから成る XML ドキュメントを含むデータの文字列を返しますが、これで何が可能でしょう。コントロールによっては、XML に直接バインドできるものもあります。返された XML 全体を解析し、個々のデータ要素に対処することも可能です。

図 7 は、返された結果をループ処理する例を示します。このコードは、XML ドキュメントに文字列を読み込み、データをループ処理する方法を示しています。Silverlight で XML ドキュメントを操作する際に最も万能な機能は XDocument クラスにあります。このクラスにアクセスするには、Silverlight プロジェクトに System.Xml.Linq への参照を追加します。

図 7 XDocuments の操作

void proxy_GetCRMDataCompleted(object sender, GetCRMDataCompletedEventArgs e)
{
  XDocument xDoc = XDocument.Parse(e.Result);
 
  string firstName;
  string lastName;
  string ID;
  string title;
 
  // Loop through the results.
  foreach (XElement element in xDoc.Descendants("User"))
  {
    firstName = GetAttributeValue(element, "FirstName");
    lastName = GetAttributeValue(element, "LastName");
    ID = GetAttributeValue(element, "SystemUserId");
    title = GetAttributeValue(element, "JobTitle");
  }
}
 
private string GetAttributeValue(XElement element, string strAttributeName)
{
  if (element.Attribute(strAttributeName) != null)
  {
    return element.Attribute(strAttributeName).Value;
  }
  return string.Empty;
}

無限の可能性

2 つのテクノロジを組み合わせると、可能性は無限大に広がります。統合後は、例外処理 (多くの Silverlight コントロールは例外を隠すため、状況に応じてこれに対処する必要があります) を行ったり、さまざまなコントロールとの統合を行ったりすることも可能です。ただし、次に何を行う場合でも、CRM 4.0 を使ってデータの読み書きを行う Silverlight ソリューションを構築するために必要なものはすべてお見せしました。

Mark Beckner は、Inotek Consulting Group LLC の創設者です。BizTalk、SharePoint、Dynamics CRM、.NET Framework 開発全般など、さまざまな Microsoft テクノロジに携わっています。連絡先は mmbeckner@inotekgroup.com (英語のみ) です。

この記事のレビューに協力してくれた技術スタッフの Scott Jones に心より感謝いたします。