ドメイン固有言語における検証
ドメイン固有言語の作成者 (DSL) はユーザーが作成したモデルが有効であることを確認する検証制約を定義できます。たとえばユーザーとその先祖の系譜を描画 DSL のユーザーが親は子の後で誕生日を持つようにする制約を作成できます。
開くときにユーザーが明示的に ENT2ENT [入力] メニューのを実行するときにモデルを格納する検証制約を実行させることができます。プログラムの制御下で検証を実行できます。たとえばプロパティ値または関係の変更に応答して検証を実行できます。
検証はユーザー モデルを操作する他のツールまたはテキスト テンプレートを作成して特に重要です。検証はモデルがこれらのツールで想定する必要条件を満たす必要があります。
注意 |
---|
検証制約は拡張子のメニュー コマンドとジェスチャ ハンドラーとともに DSL に別の拡張子で定義されるを許可できます。ユーザーはDSL にこれらの拡張機能をインストールするように選択できます。詳細については、「MEF による DSL の拡張」を参照してください。 |
実行中の検証
ユーザーがモデルやドメイン固有言語 (つまりインスタンスを編集すると次のアクションが検証を実行できます :
ダイアグラムを右クリックし[ENT0ENT] を選択します。
DSL のエクスプローラーのノードを右クリックしを選択します ENT0ENT [入力]
モデルを保存します。
モデルを開きます。
またたとえばメニュー コマンドの一部としてまたは変更に応答して検証を実行するプログラム コードを記述できます。
すべての検証エラーが ENT0ENT [出力] ウィンドウに表示されます。ユーザーはエラーの原因になるモデル要素を選択するエラー メッセージを各クリックできます。
検証制約の定義
DSL のドメイン クラスまたはリレーションシップに検証メソッドを追加することによって検証制約を定義します。検証がユーザーによってプログラムの制御で実行されている場合検証メソッドの一部またはすべてが実行されます。各メソッドはクラスの各インスタンスに適用され各クラスには複数の検証メソッドがあります。
それぞれの検証メソッドが検出されませんでしたエラーを報告します。
[!メモ]
検証メソッドはエラーを報告しますがモデルは変更されません。特定の変更を調整するかようにするには 検証に関して を参照してください。
検証制約を定義するには
Editor\Validation ノードの検証 :
Dsl\DslDefinition.dsl を起動します。
DSL のエクスプローラーでENT0ENT [入力] ノードを展開し[ENT1ENT] を選択します。
[プロパティ] ウィンドウでtrue に *** Uses *** のプロパティを設定します。これらのプロパティを設定すると便利です。
ソリューション エクスプローラーのツール バーの [ENT0ENT] をクリックします。
ドメイン クラスまたはドメイン リレーションシップの一つ以上の部分クラス定義を作成します。Dsl プロジェクトの新しいコード ファイルの定義を作成します。
この属性で各クラスの前に : 付けます。
[ValidationState(ValidationState.Enabled)]
- 既定ではこの属性は派生クラスの検証を有効にします。特定の派生クラスの検証を無効にするにはValidationState.Disabled を使用できます。
クラスに検証を追加するメソッド。各検証メソッドの名前を使用できますが ValidationContext の 1 種類の型パラメーターがあります。
これは ValidationMethod の属性が付けられている必要があります :
[ValidationMethod (ValidationCategories.Open | ValidationCategories.Save | ValidationCategories.Menu ) ]
ValidationCategories はメソッドが実行されるたびに指定します。
次に例を示します。
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Validation;
// Allow validation methods in this class:
[ValidationState(ValidationState.Enabled)]
// In this DSL, ParentsHaveChildren is a domain relationship
// from Person to Person:
public partial class ParentsHaveChildren
{
// Identify the method as a validation method:
[ValidationMethod
( // Specify which events cause the method to be invoked:
ValidationCategories.Open // On file load.
| ValidationCategories.Save // On save to file.
| ValidationCategories.Menu // On user menu command.
)]
// This method is applied to each instance of the
// type (and its subtypes) in a model:
private void ValidateParentBirth(ValidationContext context)
{
// In this DSL, the role names of this relationship
// are "Child" and "Parent":
if (this.Child.BirthYear < this.Parent.BirthYear
// Allow user to leave the year unset:
&& this.Child.BirthYear != 0)
{
context.LogError(
// Description:
"Child must be born after Parent",
// Unique code for this error:
"FAB001ParentBirthError",
// Objects to select when user double-clicks error:
this.Child,
this.Parent);
}
}
このコードについては次の点に注意してください :
ドメイン クラスまたはドメイン リレーションシップに検証メソッドを追加できます。次のコードはDsl\Generated Code\Domain*.cs に入力します。
各検証メソッドはクラスとサブクラスのすべてのインスタンスに適用されます。ドメイン リレーションシップの場合各インスタンスは2 種類のモデル要素間のリンクです。
検証メソッドは指定された順序で適用されず各メソッドは予測可能な順序でクラスのインスタンスに適用されません。
通常はこれにより矛盾した結果が生じるためストアのコンテンツを更新する検証メソッドの意図することです。代わりにこのメソッドは context.LogErrorLogWarning または LogInfo を呼び出してエラーを報告します。
LogError の呼び出しではユーザーがエラー メッセージを各クリックすると選択したリレーションシップのリンクまたはモデル要素のリストを提供できます。
プログラム コードでモデルを読み込む方法についてはプログラム コードにおけるモデル内の移動およびモデルの更新 を参照してください。
次の例はドメイン モデルに適用されます。ParentsHaveChildren のリレーションシップに名をもらった子と親である必要があります。
検証カテゴリ
ValidationMethod の属性では検証メソッドがいつ実行するかを指定します。
[カテゴリ] |
実行 |
---|---|
ユーザーが検証メニュー コマンドを起動します。 |
|
モデル ファイルを開くとき。 |
|
ファイルが格納されます。検証エラーがある場合ユーザーは保存操作をキャンセルするオプションです。 |
|
ファイルが格納されます。このカテゴリのメソッドからエラーがある場合はファイルを再度開くことができない可能性があるユーザーは警告が表示されます。 同じ名前または ID をテストするまたは読み込みエラーが発生する可能性がある他の条件使用して検証メソッドにこのカテゴリの。 |
|
ValidateCustom のメソッドが呼び出されたとき。このカテゴリの検証はプログラム コードからのみ呼び出すことができます。 詳細については カスタム検証カテゴリ を参照してください。 |
検証メソッドを配置する場所を
通常異なる種類の検証メソッドを追加することにより同じ効果を実現できます。たとえばParentsHaveChildren の関係ではなくユーザーのクラスにメソッドを追加しそれをリンクを反復処理されます :
[ValidationState(ValidationState.Enabled)]
public partial class Person
{[ValidationMethod
( ValidationCategories.Open
| ValidationCategories.Save
| ValidationCategories.Menu
)
]
private void ValidateParentBirth(ValidationContext context)
{
// Iterate through ParentHasChildren links:
foreach (Person parent in this.Parents)
{
if (this.BirthYear <= parent.BirthYear)
{ ...
**検証制約の総計。**予測可能な順序で検証を適用するには所有者クラスがそのうちの一つの検証メソッドをモデルのルート要素を定義します。この方法は一つのメッセージに複数のエラー レポートを集約することができます。
欠点はメソッドの管理がよりも簡単制約は同じ ValidationCategories があることです。したがって可能であれば別のメソッドで各制約を保持することをお勧めします。
**コンテキスト キャッシュの値を渡します。**コンテキスト パラメーターには任意の値を配置できるディクショナリがあります。ディクショナリには検証を実行する有効期間中保持されます。特定の検証メソッドはコンテキストなどでエラー番号をクリックし繰り返しメッセージがエラー ウィンドウにオーバーフローを回避するために使用できます。次に例を示します。
List<ParentsHaveChildren> erroneousLinks;
if (!context.TryGetCacheValue("erroneousLinks", out erroneousLinks))
erroneousLinks = new List<ParentsHaveChildren>();
erroneousLinks.Add(this);
context.SetCacheValue("erroneousLinks", erroneousLinks);
if (erroneousLinks.Count < 5) { context.LogError( ... ); }
多重度の検証
最小の多重度を確認するための検証メソッドはDSL に自動的に生成されます。コードは Dsl\Generated Code\MultiplicityValidation.cs に書き込まれます。これらのメソッドはDSL エクスプローラーの [ENT0ENT] ノードの検証を有効にする場合に適用されます。
1. * または 1..1 にドメイン リレーションシップのロールの多重度を設定しますがユーザーがこのリレーションシップのリンクを作成する検証エラー メッセージが表示されます。
町のロールで町を持たない各ユーザーのリレーションシップ 1..* の DSL たとえばクラスのユーザーと町がある場合はリレーションシップ PersonLivesInTownエラー メッセージが表示されます。
プログラム コードの連続した検証
ValidationController のアクセスや作成して検証を実行できます。エラーがエラー ウィンドウにユーザーに表示する場合は図の DocData にアタッチされている ValidationController を使用します。たとえばメニュー コマンドを作成した場合CurrentDocData.ValidationController はコマンドのクラスにあります :
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Validation;
using Microsoft.VisualStudio.Modeling.Shell;
...
partial class MyLanguageCommandSet
{
private void OnMenuMyContextMenuCommand(object sender, EventArgs e)
{
ValidationController controller = this.CurrentDocData.ValidationController;
...
詳細については、「方法: ショートカット メニューにコマンドを追加する」を参照してください。
別の検証コントローラーの作成エラーを自分で管理します。次に例を示します。
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Validation;
using Microsoft.VisualStudio.Modeling.Shell;
...
Store store = ...;
VsValidationController validator = new VsValidationController(s);
// Validate all elements in the Store:
if (!validator.Validate(store, ValidationCategories.Save))
{
// Deal with errors:
foreach (ValidationMessage message in validator.ValidationMessages) { ... }
}
変更が発生したときに実行する検証
モデルが無効になっている場合ユーザーは警告いることを確認するにはその実行の検証ストアのイベントを定義できます。ストアのイベントの詳細についてはイベント ハンドラーによって変更内容がモデル外に反映される を参照してください。
検証コードに加えて次の例のようなコンテンツの DslPackage プロジェクトでのカスタム コード ファイルを追加します。ドキュメントに追加されるこのコードは ValidationController を使用します。このコントローラーは Visual Studio の [エラー一覧] 検証エラーが表示されます。
using System;
using System.Linq;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Validation;
namespace Company.FamilyTree
{
partial class FamilyTreeDocData // Change name to your DocData.
{
// Register the store event handler:
protected override void OnDocumentLoaded()
{
base.OnDocumentLoaded();
DomainClassInfo observedLinkInfo = this.Store.DomainDataDirectory
.FindDomainClass(typeof(ParentsHaveChildren));
DomainClassInfo observedClassInfo = this.Store.DomainDataDirectory
.FindDomainClass(typeof(Person));
EventManagerDirectory events = this.Store.EventManagerDirectory;
events.ElementAdded
.Add(observedLinkInfo, new EventHandler<ElementAddedEventArgs>(ParentLinkAddedHandler));
events.ElementDeleted.Add(observedLinkInfo, new EventHandler<ElementDeletedEventArgs>(ParentLinkDeletedHandler));
events.ElementPropertyChanged.Add(observedClassInfo, new EventHandler<ElementPropertyChangedEventArgs>(BirthDateChangedHandler));
}
// Handler will be called after transaction that creates a link:
private void ParentLinkAddedHandler(object sender,
ElementAddedEventArgs e)
{
this.ValidationController.Validate(e.ModelElement,
ValidationCategories.Save);
}
// Called when a link is deleted:
private void ParentLinkDeletedHandler(object sender,
ElementDeletedEventArgs e)
{
// Don't apply validation to a deleted item!
// - Validate store to refresh the error list.
this.ValidationController.Validate(this.Store,
ValidationCategories.Save);
}
// Called when any property of a Person element changes:
private void BirthDateChangedHandler(object sender,
ElementPropertyChangedEventArgs e)
{
Person person = e.ModelElement as Person;
// Not interested in changes in other properties:
if (e.DomainProperty.Id != Person.BirthYearDomainPropertyId)
return;
// Validate all parent links to and from the person:
this.ValidationController.Validate(
ParentsHaveChildren.GetLinksToParents(person)
.Concat(ParentsHaveChildren.GetLinksToChildren(person))
, ValidationCategories.Save);
}
}
}
ハンドラーは元の後に呼び出されるかまたはリンク要素に影響する操作をやり直してから/LTCG。
カスタム検証カテゴリ
標準の検証のカテゴリに加えてメニューから開きますなど独自のカテゴリを定義できます。プログラム コードからこれらのカテゴリを呼び出すことができます。ユーザーはそれらを直接呼び出すことはできません。
カスタム カテゴリの一般的な使用方法はモデルが特定のツールの事前条件を満たすかどうかなどのテスト カテゴリを定義することです。
特定のカテゴリに検証メソッドを追加するにはこのような属性でその前に : 付けます。
[ValidationMethod(CustomCategory = "PreconditionsForGeneratePartsList")]
[ValidationMethod(ValidationCategory.Menu)]
private void TestForCircularLinks(ValidationContext context)
{...}
[!メモ]
必要に応じて [ValidationMethod()] の属性とメソッドの前に付けることができます。標準およびカスタム カテゴリにメソッドを追加できます。
カスタム検証を呼び出す場合 :
// Invoke all validation methods in a custom category:
validationController.ValidateCustom
(store, // or a list of model elements
"PreconditionsForGeneratePartsList");
検証に関して
検証制約はエラーを報告しますがモデルは変更されません。代わりに無効なモデルを永続的に禁止する場合は他の技法を使用できます。
ただしこの手法は推奨されていません。通常はユーザーが決定できるようにすることをお勧めします。無効なモデルを解決する方法を示します。
**有効性にモデルを復元する変更を調整します。**たとえばユーザーがの値が最大に許容される最大のプロパティプロパティをリセットできます。これを行うには規則を定義します。詳細については、「規則によって変更内容がモデル内に反映される」を参照してください。
**無効な変更がトランザクションをロールバックします。**このためにこの規則を定義できますが場合によってはプロパティ *** OnValueChanging() *** ハンドラーをオーバーライドすることもできます。または OnDeleted(). などのメソッドをオーバーライドするとトランザクション詳細についてはを使用 this.Store.TransactionManager.CurrentTransaction.Rollback(). は" " を参照 ドメイン プロパティ値変更ハンドラー をロールバックします。
注意 |
---|
変更が調整されているかまたはコピーされたことをユーザーが確認してください。たとえばSystem.Windows.Forms.MessageBox.Show("message"). を使用します。 |