プロジェクトを比較する
プロジェクトの設計モデルの変更内容は、変更前後のプロジェクトを比較し、その比較結果を評価することで確認することができます。
変更前後のプロジェクトの比較には、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}");
}
プロジェクトの比較は、2way比較です。そのため、IDiff.ComputeModelsメソッドに与えるプロジェクトの順序を入れ替えた場合、差分抽出の結果が反転します。
差分抽出モデル
Next Design では、比較対象の2つのプロジェクトに対して、設計モデルの識別子単位で比較を行います。この比較結果は差分抽出モデルとして管理します。

| モデル | 説明 | 
|---|---|
| IDiff | プロジェクトの比較を実行するインタフェース、および前回の比較結果を取得するインタフェースを提供します。 | 
| IModelComparison | 任意のプロジェクト間の比較結果を管理する処理単位オブジェクトです。すべての設計モデルの比較結果を保持します。 BeforeProjectプロパティで変更前のプロジェクトに、AfterProjectプロパティで変更後のプロジェクトにアクセスできます。 | 
| IMatch | 設計モデル単位の比較結果オブジェクトです。 Targetプロパティで変更前のモデルに、Referenceプロパティで変更後のモデルにアクセスできます。 | 
| IDifference | 差分情報オブジェクトです。このオブジェクトを参照することで設計モデルの変更内容を確認することができます。更新差分として抽出した場合は、更新したフィールド名、および更新前後の値を確認できます。 | 
比較実施時のメタモデル構造は、変更後のプロジェクトのプロファイルを基準に評価します。そのため、比較対象のプロジェクト間で、プロファイルに対する変更があった場合、正しく差分が抽出できない場合があります。
クラス型フィールドの更新と差分情報
クラス型フィールドの更新差分情報では、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}");
        }
    }
}
削除されたモデルは、変更後のプロジェクトには存在しないため、変更後のプロジェクトからは取得できません。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}");
        }
    }
}