メインコンテンツまでスキップ

プロジェクトを比較する

プロジェクトの設計モデルの変更内容は、変更前後のプロジェクトを比較し、その比較結果を評価することで確認することができます。 変更前後のプロジェクトの比較には、IDiffオブジェクトのComputeModelsメソッドを用います。

  • プロジェクトを比較した結果は差分抽出モデルとして取得できます
  • 差分抽出モデルを用いて、プロジェクト間でどのような変更が行われたかを確認することができます
public void ComputeProjects(ICommandContext c, ICommandParams p)
{
// カレントプロジェクトと比較するプロジェクトを開きます
var projectPath = c.App.Window.UI.ShowOpenFileDialog("比較するプロジェクトを開く", "プロジェクトファイル (*.nproj)|*.nproj|すべてのファイル (*.*)|*.*");
if (projectPath == null)
{
// キャンセルされた場合は処理を終了します
return;
}
// 比較するプロジェクトは、カレントに設定しないように指定します
var diffProject = c.App.Workspace.OpenProject(projectPath, false);

// プロジェクトを比較します
var project = c.App.Workspace.CurrentProject;
IModelComparison comparison = c.App.Diff.ComputeModels(project, diffProject);

// 差分抽出件数を出力します
c.App.Output.WriteLine("差分比較", $"差分抽出件数={comparison.Differences.Count}");
}
note

プロジェクトの比較は、2way比較です。そのため、IDiff.ComputeModelsメソッドに与えるプロジェクトの順序を入れ替えた場合、差分抽出の結果が反転します。

差分抽出モデル

Next Design では、比較対象の2つのプロジェクトに対して、設計モデルの識別子単位で比較を行います。この比較結果は差分抽出モデルとして管理します。

差分抽出モデル

モデル説明
IDiffプロジェクトの比較を実行するインタフェース、および前回の比較結果を取得するインタフェースを提供します。
IModelComparison任意のプロジェクト間の比較結果を管理する処理単位オブジェクトです。すべての設計モデルの比較結果を保持します。BeforeProjectプロパティで変更前のプロジェクトに、AfterProjectプロパティで変更後のプロジェクトにアクセスできます。
IMatch設計モデル単位の比較結果オブジェクトです。Targetプロパティで変更前のモデルに、Referenceプロパティで変更後のモデルにアクセスできます。
IDifference差分情報オブジェクトです。このオブジェクトを参照することで設計モデルの変更内容を確認することができます。更新差分として抽出した場合は、更新したフィールド名、および更新前後の値を確認できます。
info

比較実施時のメタモデル構造は、変更後のプロジェクトのプロファイルを基準に評価します。そのため、比較対象のプロジェクト間で、プロファイルに対する変更があった場合、正しく差分が抽出できない場合があります。

クラス型フィールドの更新と差分情報
クラス型フィールドの更新差分情報では、OldValueプロパティ、およびNewValueプロパティに、それぞれ変更前後のフィールドでの関連オブジェクトの識別子リストが記録されます。実際の関連元や関連先は、リストの識別子から関連オブジェクトを特定してたどることができます。

以下は、クラス型フィールドの更新差分から、新しく関連づけられたモデル、および関連づけが解除されたモデルを一覧する例です。

public void CheckModelMoved(ICommandContext c, ICommandParams p)
{
// 現在のプロジェクトの比較結果を取得します
var project = c.App.Workspace.CurrentProject;
IModelComparison comparison = c.App.Diff.GetComparison(project);

// 変更前のプロジェクトを取得します
var beforeProject = comparison.BeforeProject;

// 選択したモデルの所有元との所有関連取得します
var model = c.App.Workspace.CurrentModel;

// モデルの差分を取得します。
IMatch match = comparison.GetMatch(model);

if (!match.Differences.Any(d => d.IsUpdateItem))
{
return;
}

foreach (IDifference difference in match.Differences)
{
var field = difference.Field;
// クラス型フィールドの差分は、stringのListで記録されます
var beforeRelationships = difference.OldValue as IList<string>;
var afterRelationships = difference.NewValue as IList<string>;
if (beforeRelationships == null || afterRelationships == null)
{
continue;
}

c.App.Output.WriteLine("差分比較", $"[更新]{model.Name}の{field}");

// 追加された関連の識別子
var addRelationships = afterRelationships.Except(beforeRelationships);
foreach (string relationshipId in addRelationships)
{
// 変更後のプロジェクトから識別子を指定して関連を取得します
IRelationship relationship = project.GetRelationshipById(relationshipId);
// 関連から相手側のモデルを取得します
IModel related = model.Equals(relationship.Source) ? relationship.Target : relationship.Source;
c.App.Output.WriteLine("差分比較", $"{related.Name}との関連が追加されました。");
}

// 削除された関連の識別子
var deleteRelationships = beforeRelationships.Except(afterRelationships);
foreach (string relationshipId in deleteRelationships)
{
// 変更前のプロジェクトから識別子を指定してモデルと関連を取得します
IModel beforeModel = beforeProject.GetModelById(model.Id);
IRelationship relationship = beforeProject.GetRelationshipById(relationshipId);
// 関連から相手側のモデルを取得します
IModel related = beforeModel.Equals(relationship.Source) ? relationship.Target : relationship.Source;
c.App.Output.WriteLine("差分比較", $"{related.Name}との関連が削除されました。");
}
}
}

関連の関連端の差分情報
関連オブジェクトで抽出した更新差分情報では、メタモデルで定義するフィールドとは別に、関連端の差分も通知されます。差分情報が関連端の差分であるかは、IDifference.Fieldプロパティを調べることで確認できます。

フィールド名内容
@SourceId関連元の差分を表します。OldValueには、変更前の関連元の識別子、NewValueには変更後の関連元の識別子が記録されます。
@TargetId関連先の差分を表します。OldValueには、変更前の関連先の識別子、NewValueには変更後の関連先の識別子が記録されます。

追加した設計モデルを一覧する

以下は、差分抽出モデルから、変更後のプロジェクトで追加したモデルを一覧する例です。追加した設計モデルは、IMatch.DifferencesプロパティにIDifference.IsNewItemプロパティがtrueの差分が記録されます。

public void GetAddedModels(ICommandContext c, ICommandParams p)
{
// 現在のプロジェクトの比較結果を取得します
var project = c.App.Workspace.CurrentProject;
IModelComparison comparison = c.App.Diff.GetComparison(project);

// 追加差分が検出されたモデルを一覧します。
var newItemMatches = comparison.Matches.Where(m => m.Differences.Any(d => d.IsNewItem));
foreach (IMatch match in newItemMatches)
{
// 追加したモデルは、IMatch.Referenceに記録されています
var model = match.Reference;
if (model is IRelationship relationship)
{
// 関連オブジェクトの比較結果の場合は、IRelationshipにキャストできます
c.App.Output.WriteLine("差分比較", $"[追加]{relationship.Source.Name}と{relationship.Target.Name}の関連");
}
else
{
c.App.Output.WriteLine("差分比較", $"[追加]{model.Name}");
}
}
}

削除した設計モデルを一覧する

以下は、差分抽出モデルから、変更後のプロジェクトで削除したモデルを一覧する例です。削除した設計モデルは、IMatch.DifferencesプロパティにIDifference.IsOldItemプロパティがtrueの差分が記録されます。

public void GetDeletedModels(ICommandContext c, ICommandParams p)
{
// 現在のプロジェクトの比較結果を取得します
var project = c.App.Workspace.CurrentProject;
IModelComparison comparison = c.App.Diff.GetComparison(project);

// 削除差分が検出されたモデルを一覧します。
var oldItemMatches = comparison.Matches.Where(m => m.Differences.Any(d => d.IsOldItem));
foreach (IMatch match in oldItemMatches)
{
// 削除したモデルは、IMatch.Targetに記録されています
var model = match.Target;
if (model is IRelationship relationship)
{
// 関連オブジェクトの比較結果の場合は、IRelationshipにキャストできます
c.App.Output.WriteLine("差分比較", $"[削除]{relationship.Source.Name}と{relationship.Target.Name}の関連");
}
else
{
c.App.Output.WriteLine("差分比較", $"[削除]{model.Name}");
}
}
}
tip

削除されたモデルは、変更後のプロジェクトには存在しないため、変更後のプロジェクトからは取得できません。IModelComparison.BeforeProjectプロパティで変更前のプロジェクトにアクセスできます。

更新した設計モデルを一覧する

以下は、差分抽出モデルから、変更後のプロジェクトで更新したモデルを一覧する例です。削除した設計モデルは、IMatch.DifferencesプロパティにIDifference.IsUpdateItemプロパティがtrueの差分が記録されます。

public void GetUpdateModels(ICommandContext c, ICommandParams p)
{
// 現在のプロジェクトの比較結果を取得します
var project = c.App.Workspace.CurrentProject;
IModelComparison comparison = c.App.Diff.GetComparison(project);

// 更新差分が検出されたモデルを一覧します。
var updateItemMatches = comparison.Matches.Where(m => m.Differences.Any(d => d.IsUpdateItem));
foreach (IMatch match in updateItemMatches)
{
// 更新後のモデルは、IMatch.Targetに記録されています
var model = match.Target;
if (model is IRelationship relationship)
{
// 関連オブジェクトの比較結果の場合は、IRelationshipにキャストできます
c.App.Output.WriteLine("差分比較", $"[更新]{relationship.Source.Name}と{relationship.Target.Name}の関連");
}
else
{
c.App.Output.WriteLine("差分比較", $"[更新]{model.Name}");
}

// フィールド毎の差分を出力します
var differences = match.Differences.Where(d => d.IsUpdateItem);
foreach (IDifference difference in differences)
{
var field = difference.Field;
var oldValue = difference.OldValue;
var newValue = difference.NewValue;
if (oldValue is IList<string> oldValues && newValue is IList<string> newValues)
{
c.App.Output.WriteLine("差分比較", $" - {field}の要素数:{oldValues.Count} -> {newValues.Count}");
continue;
}
c.App.Output.WriteLine("差分比較", $" - {field}:{oldValue} -> {newValue}");
}
}
}

移動した設計モデルを一覧する

以下は、差分抽出モデルから、変更後のプロジェクトで移動したモデルを一覧する例です。モデルの移動は、関連オブジェクトの差分情報から取得します。

モデルの移動に対する差分

V2.0 では、比較前後のプロジェクトでモデルを移動した場合の差分は、移動を行ったモデルでは抽出されませんIDifference.IsMoveItemプロパティでは評価できないためご注意ください。

public void GetMovedModels(ICommandContext c, ICommandParams p)
{
// 現在のプロジェクトの比較結果を取得します
var project = c.App.Workspace.CurrentProject;
IModelComparison comparison = c.App.Diff.GetComparison(project);

// 更新差分が検出された関連モデルを取得します。
var updateRelationshipMatches = comparison.Matches.Where(m => m.Target is IRelationship && m.Differences.Any(d => d.IsUpdateItem));
foreach (IMatch match in updateRelationshipMatches)
{
// 更新後の関連モデルは、IMatch.Targetに記録されています
var relationship = (IRelationship)match.Target;
if (!relationship.IsEmbedded)
{
// 所有関連でなければスキップ
continue;
}
var difference = match.Differences.FirstOrDefault(d => d.Field == "@SourceId");
if (difference == null)
{
// 関連元で差分がなければスキップ
continue;
}
// 移動したモデルは、関連の関連先で取得できます
var model = relationship.Target;
// 移動後の所有元は、関連の関連元で取得できます
var newParent = relationship.Source;
// 移動前の所有元は、変更前の関連の関連元で取得できます
var oldRelationship = (IRelationship)match.Reference;
var oldParent = oldRelationship.Source;

c.App.Output.WriteLine("差分比較", $"[移動]{model.Name}:{oldParent.Name} ==> {newParent.Name}");
}
}

差分情報から設計モデルの移動を検出する

設計モデルを基点にして、モデルが移動されたかを評価する場合は、IModel.GetOwnerRelationship()で取得できる所有元との関連オブジェクトの差分情報から検出することができます。所有元との関連オブジェクトの差分において、@SourceIdフィールドに更新差分がある場合には、設計モデルの所有元が変更されたことになります。

public void CheckModelMoved(ICommandContext c, ICommandParams p)
{
// 現在のプロジェクトの比較結果を取得します
var project = c.App.Workspace.CurrentProject;
IModelComparison comparison = c.App.Diff.GetComparison(project);

// 選択したモデルの所有元との所有関連取得します
var model = c.App.Workspace.CurrentModel;
var ownerRelationship = model.GetOwnerRelationship();

// 所有関連の差分を取得します。
IMatch match = comparison.GetMatch(ownerRelationship);

if (!match.HasDifference)
{
c.App.Output.WriteLine("差分比較", $"{model.Name}は移動されていません。");
return;
}

// 所有関連の関連元の差分を取得します
var difference = match.Differences.FirstOrDefault(d => d.IsUpdateItem && d.Field == "@SourceId");
if (difference == null)
{
c.App.Output.WriteLine("差分比較", $"{model.Name}は移動されていません。");
return;
}

// 移動後の所有元は、関連の関連元で取得できます
var newRelationship = (IRelationship)match.Target;
var newParent = newRelationship.Source;
// 移動前の所有元は、変更前の関連の関連元で取得できます
var oldRelationship = (IRelationship)match.Reference;
var oldParent = oldRelationship.Source;

c.App.Output.WriteLine("差分比較", $"{model.Name}は、{oldParent.Name} から {newParent.Name} に移動されました。");
}