Dynamics CRM 個人ビューのエクスポートとインポート その 1
※2015/7/21 ユーザービュー取得時の実行ユーザー偽装コードを追加しました。
みなさん、こんにちは。
今日は開発者向けの情報として、個人ビューのエクスポートと
インポートを考察した結果とサンプルコードを公開します。尚
対象は Microsoft Dynamics CRM 2013 および同等バージョンの
Microsoft Dynamics CRM Online とします。
今回はまず個人ビューのエクスポートを考えます。
※以下で紹介する内容はあくまで個人の見解であり、コードは
自己責任で検証を行ってください。
概要:個人ビュー移行の問題
テスト環境から UAT や本番環境にソリューションを移行する場合
通常はソリューションのエクスポートとインポートでカスタマイズ
を移行し、データのエクスポートとインポートでデータを移行する
事になりますが、よく課題となるのが「個人ビュー」です。
個人ビューはソリューションで移行が行えず、またデータ移行も
行えないため、通常は手動で再作成となりますが、ユーザー数や
ビューの数から現実的でない場合があります。そこで今回は
SDK を利用して個人ビューの移動が行えるか検討してみます。
個人ビューエンティティ
個人ビューは UserQuery エンティティ に格納されています。メタ
データを確認すると、以下フィールドに重要な情報が格納されて
いることが分かります。
ColumnSetXml : ビューで利用される列の情報
FetchXml:ビューで利用されるクエリの情報
LayoutXml:ビューで利用されるレイアウト情報
OwnerId:ビューの所有者
QueryType:ビューのタイプ
ReturnedTypeCode:ビューの対象エンティティ
ビューのタイプの関する情報はこちら。
環境によって値が変わる要素は OwnerId と ReturnedTypeCode
(カスタムエンティティ) であると想定します。またエンティティ
用の個人ビューは QueryType = 0 であると仮置きします。
データの取得
では早速 SDK でデータを取得してみます。以下は QueryExpression
を利用したクエリの例です。
// QueryExpression の生成
QueryExpression query = new QueryExpression();
// エンティティ名を指定
query.EntityName = UserQuery.EntityLogicalName;
// すべての列を取得
query.ColumnSet = new ColumnSet(true);
// 条件を作成
query.Criteria = new FilterExpression();
// 条件として QueryType = 0 を指定
query.Criteria.AddCondition(new ConditionExpression("querytype", ConditionOperator.Equal, 0));
// 結果を取得
var results = _serviceProxy.RetrieveMultiple(query);
上記クエリで取得した結果の 1 件以下に表示します。
まず気が付くのは、ReturnedTypeCode が数値ではなく、
エンティティの論理名として返ってきています。また
OwnerId は Guid ではなく、EntityReference です。
次にカスタムエンティティの個人ビューを以下に示します。
やはり ReturnedTypeCode はエンティティの論理名です。しかし
その一方で LayoutXml には object=”10005” という文字列が見える
ため、エンティティの ObjectTypeCode を含んでいる列がある事も
見て取れます。
データの保存
データの取得が行えることはわかったので、後でインポートをする
ためにデータをディスクに保存してみます。また後から情報も紐付
を変更できるよう、ユーザー単位で個人ビューを取得して、ビュー
の所有者を特定する一意の値として DomainName 列を利用します。
作成したコードは以下の通りです。これでプログラムの実行フォルダ
に「UserQuery_ドメイン名.xml」のファイルが作成されます。
// 有効なユーザーを取得
// FetchXML は高度な検索より取得
EntityCollection resutls = _serviceProxy.RetrieveMultiple(new FetchExpression(@"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
<entity name='systemuser'>
<attribute name='domainname' />
<filter type='and'>
<condition attribute='isdisabled' operator='eq' value='0' />
<condition attribute='accessmode' operator='ne' value='3' />
</filter>
</entity>
</fetch>"));
// ユーザーごとに個人ビューを取得
foreach (var result in resutls.Entities)
{
// 取得したレコードを SystemUser に変換
SystemUser user = result.ToEntity<SystemUser>();
// QueryExpression の生成
QueryExpression query = new QueryExpression();
// エンティティ名を指定
query.EntityName = UserQuery.EntityLogicalName;
// すべての列を取得
query.ColumnSet = new ColumnSet(true);
// 条件を作成
query.Criteria = new FilterExpression();
// 条件として QueryType = 0 を指定
query.Criteria.AddCondition(new ConditionExpression("querytype", ConditionOperator.Equal, 0));
// 条件として所有者を指定
query.Criteria.AddCondition(new ConditionExpression("ownerid", ConditionOperator.Equal, user.Id));
// 実行ユーザーを偽装 ※2015/07/21 追加
_serviceProxy.CallerId = user.Id;
// 結果を取得
EntityCollection views = _serviceProxy.RetrieveMultiple(query);
// ビューが 1 件もない場合は次のユーザーの処理
if (views.Entities.Count == 0)
continue;
// シリアライザーを作成
DataContractSerializer serializer = new DataContractSerializer(typeof(EntityCollection));
// データの書き出し
using (MemoryStream ms = new MemoryStream())
{
// データをシリアライズ
serializer.WriteObject(ms, views);
// シリアライズした結果を文字列に変換
var xml = Encoding.UTF8.GetString(ms.ToArray(), 0, (int)ms.Length);
// DomainName を利用してファイルを作成し保存
StreamWriter sw = new StreamWriter("UserQuery_" + user.DomainName + ".xml");
sw.Write(xml);
sw.Close();
}
}
保存されたファイルを XML エディターで開いて確認してください。
次回は保存したファイルを再度オブジェクト化して、新しい環境に
インポートする考察を行います。
- 中村 憲一郎
Comments
Anonymous
July 09, 2015
ユーザーは自分のビューしか取られないですから、実行中別のユーザーに成りすまさないとデータが取れないです。 _serviceProxy.CallerId = user.Id;Anonymous
July 20, 2015
コメントありがとうございます。 ご指摘いただいた部分を修正しました。 中村 憲一郎