網域指定的語言中的驗證
身為定義域專屬語言 (DSL) 的撰寫者,您可以定義來驗證使用者所建立的模型是有意義的驗證條件約束。比方說,如果您的 DSL 允洏峈繪製族譜的人員和他們的祖系,您可以撰寫一個限制式,可確保個子系有父系之後的出生日期。
您可以讓執行和儲存模型後,當它開啟時,當使用者明確執行的驗證條件約束驗證功能表命令。您也可以執行程式控制下的驗證。比方說,您無法變更屬性值或關聯性的回應而執行驗證。
驗證是特別重要,如果您正在撰寫文字範本或其他工具,處理您的使用者模型。驗證可確保模型滿足假設這些工具的先決條件。
警告 |
---|
您也可以允許在不同的擴充程式到您的 DSL,連同擴充功能表命令和筆勢的處理常式中定義的驗證條件約束。使用者可以選擇要安裝這些擴充功能,除了您的 DSL。如需詳細資訊,請參閱 使用 MEF 擴充您的 DSL。 |
執行驗證
當使用者正在編輯的模型時,也就是執行個體的定義域專屬語言中,下列的動作可以執行驗證:
以滑鼠右鍵按一下圖表,然後選取驗證所有。
以滑鼠右鍵按一下您的 DSL 和選取的 [檔案總管] 中的最上層節點所有驗證
儲存模型。
開啟模型。
此外,您可以撰寫程式碼執行驗證,例如,功能表命令,或變更的回應。
任何驗證錯誤會出現在錯誤清單視窗。使用者可以按兩下選取的模型項目的錯誤的原因的錯誤訊息。
定義驗證條件約束
您可以加入的網域類別或您的 DSL 的關聯性的驗證方法,以定義驗證條件約束。當執行驗證時,由使用者或在程式控制項下會執行部分或所有驗證方法。每一種方法會套用至它的類別,每個執行個體,且每個類別中可以有數種驗證方法。
每個驗證方法會報告它找到的任何錯誤。
注意事項 |
---|
驗證方法報告錯誤,但不是會變更模型。如果您想要調整或避免特定變更,請參閱驗證的替代項目。 |
若要定義的驗證條件約束
啟用驗證在Editor\Validation節點:
開啟 Dsl\DslDefinition.dsl。
在 DSL 總管] 中,展開編輯器 節點並選取 驗證。
在 [屬性] 視窗中,設定會使用屬性,以true。它是最方便的方式來設定所有這些屬性。
按一下 轉換所有的範本在方案總管] 工具列中的色彩。
寫入一或多個您的網域類別或網域關聯性的部分類別定義。撰寫新程式碼檔案中的這些定義Dsl專案。
每個類別,以這個屬性的前置詞:
[ValidationState(ValidationState.Enabled)]
- 根據預設,這個屬性也可以讓衍生類別的驗證。如果您想要停用特定的衍生類別的驗證,您可以使用ValidationState.Disabled。
將驗證方法加入至類別。每一種驗證方法可以具有任何名稱,但有一個參數型別的ValidationContext。
它必須在前端加上至少有一個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。
每個驗證方法套用到它的類別和子類別的每個執行個體。如果是一種網域關係,每個執行個體是兩個模型元素之間的連結。
驗證方法不會套用任何指定的順序,以及每一種方法不會套用到任何可預測的順序其類別的執行個體。
因為這會導致不一致的結果,則通常是不好的作法,以更新存放區的內容中,驗證方法。相反地,此方法應該報告任何錯誤點撥打context.LogError, LogWarning或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。這些方法才會生效,當您啟用驗證在 Editor\Validation DSL 總管] 中的節點。
如果您將設定為 1 的網域關聯性的角色的重數..* 或 1..1,但是使用者並不會建立此關係中,連結會顯示驗證錯誤訊息。
比方說,如果您的 DSL 類別人和城鎮,並具有關聯性的關聯性 PersonLivesInTown 1..* 在城鎮角色,然後為每個人都沒有城鎮,錯誤訊息將會顯示。
從程式碼執行驗證
您可以存取或建立 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;
...
如需詳細資訊,請參閱 HOW TO:在捷徑功能表中加入命令。
您也可以建立個別的驗證控制站,並管理您自己的錯誤。例如:
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);
}
}
}
處理常式也稱為復原或取消復原作業會影響連結或項目之後。
驗證的自訂類別
除了標準的驗證類別,例如功能表和 [開啟],您可以定義自己的類別。您可以叫用這些程式碼中的類別。使用者無法進行直接叫用。
自訂類別的典型用法是定義的類別,測試是否模型滿足特定工具的先決條件。
若要新增某個指定類別目錄的驗證方法,前置詞的屬性,就像這樣:
[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"). |