Ověřování v jazyce specifickém pro doménu
Jako autor jazyka specifického pro doménu (DSL) můžete definovat ověřovací omezení, abyste ověřili, že model vytvořený uživatelem je smysluplný. Pokud například vaše DSL umožňuje uživatelům kreslit rodinný strom lidí a jejich předků, můžete napsat omezení, které zajistí, aby děti měly data narození po jejich rodičích.
Při uložení modelu, při jeho otevření a v případě, že uživatel explicitně spustí příkaz nabídky Ověřit , můžete mít spuštěná omezení ověření. Ověření můžete provést také pod kontrolou programu. Například můžete provést ověření v reakci na změnu hodnoty vlastnosti nebo relace.
Ověření je zvlášť důležité, pokud píšete textové šablony nebo jiné nástroje, které zpracovávají modely uživatelů. Ověření zajišťuje, aby modely splňovaly předpoklady, které tyto nástroje předpokládají.
Upozorňující
Můžete také povolit definování omezení ověřování v samostatných rozšířeních dsl spolu s příkazy nabídky rozšíření a obslužnými rutinami gest. Uživatelé si můžou tato rozšíření nainstalovat kromě vašeho DSL. Další informace naleznete v tématu Rozšíření DSL pomocí MEF.
Spuštění ověření
Když uživatel upravuje model, tedy instanci jazyka specifického pro vaši doménu, můžou ověření spustit následující akce:
Klikněte pravým tlačítkem myši na diagram a vyberte Ověřit vše.
Klikněte pravým tlačítkem na horní uzel v Průzkumníku vašeho DSL a vyberte Ověřit vše.
Uložte model.
Otevřete model.
Kromě toho můžete napsat programový kód, který spouští ověřování, například jako součást příkazu nabídky nebo v reakci na změnu.
Všechny chyby ověření se zobrazí v okně Seznam chyb. Uživatel může poklikáním na chybovou zprávu vybrat prvky modelu, které jsou příčinou chyby.
Definování omezení ověřování
Omezení ověřování definujete přidáním metod ověřování do tříd domény nebo relací vašeho DSL. Při spuštění ověření provádí uživatel nebo pod kontrolou programu některé nebo všechny metody ověření. Každá metoda se použije na každou instanci své třídy a v každé třídě může existovat několik metod ověřování.
Každá metoda ověřování hlásí všechny chyby, které najde.
Poznámka:
Metody ověřování hlásí chyby, ale nemění model. Pokud chcete určité změny upravit nebo zabránit, přečtěte si téma Alternativy k ověření.
Definování omezení ověřování
Povolte ověřování v uzlu Editor\Validation :
Otevřete Dsl\DslDefinition.dsl.
V Průzkumníku DSL rozbalte uzel Editoru a vyberte Možnost Ověření.
V okno Vlastnosti nastavte vlastnosti Použití na
true
. Nejpohodlnější je nastavit všechny tyto vlastnosti.Na panelu nástrojů Průzkumník řešení klikněte na Transformovat všechny šablony.
Zápis částečných definic tříd pro jednu nebo více tříd domény nebo vztahů s doménou Tyto definice zapište do nového souboru kódu v projektu Dsl .
Předpona každé třídy s tímto atributem:
[ValidationState(ValidationState.Enabled)]
- Ve výchozím nastavení tento atribut také povolí ověřování pro odvozené třídy. Pokud chcete zakázat ověřování pro určitou odvozenou třídu, můžete použít
ValidationState.Disabled
.
- Ve výchozím nastavení tento atribut také povolí ověřování pro odvozené třídy. Pokud chcete zakázat ověřování pro určitou odvozenou třídu, můžete použít
Přidejte metody ověřování do tříd. Každá metoda ověřování může mít libovolný název, ale má jeden parametr typu ValidationContext.
Musí mít předponu jeden nebo více
ValidationMethod
atributů:[ValidationMethod (ValidationCategories.Open | ValidationCategories.Save | ValidationCategories.Menu ) ]
ValidationCategories určuje, kdy je metoda spuštěna.
Příklad:
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);
}
}
Všimněte si následujících bodů o tomto kódu:
Metody ověřování můžete přidat do doménových tříd nebo vztahů mezi doménou. Kód pro tyto typy je v Dsl\Generated Code\Domain*.cs.
Každá metoda ověřování se použije na každou instanci třídy a její podtřídy. V případě vztahu mezi doménou je každá instance propojením mezi dvěma prvky modelu.
Metody ověřování nejsou použity v žádném zadaném pořadí a každá metoda není použita na instance své třídy v žádném předvídatelné pořadí.
Obvykle je pro metodu ověřování vhodné aktualizovat obsah úložiště, protože by to vedlo k nekonzistentním výsledkům. Místo toho by metoda měla hlásit jakoukoli chybu voláním
context.LogError
,LogWarning
neboLogInfo
.Ve volání LogError můžete zadat seznam prvků modelu nebo propojení relací, které budou vybrány, když uživatel dvakrát klikne na chybovou zprávu.
Informace o tom, jak číst model v kódu programu, naleznete v tématu Navigace a aktualizace modelu v kódu programu.
Příklad platí pro následující doménový model. Vztah ParentHaveChildren má role s názvem Podřízené a Nadřazené.
Kategorie ověřování
V atributu ValidationMethodAttribute určíte, kdy má být metoda ověření provedena.
Kategorie | Provádění |
---|---|
ValidationCategories | Když uživatel vyvolá příkaz nabídky Ověřit. |
ValidationCategories | Při otevření souboru modelu. |
ValidationCategories | Při uložení souboru. Pokud dojde k chybám ověření, uživateli se zobrazí možnost zrušení operace uložení. |
ValidationCategories | Při uložení souboru. Pokud v této kategorii dochází k chybám z metod, zobrazí se uživateli upozornění, že možná nebude možné soubor znovu otevřít. Tuto kategorii použijte pro metody ověřování, které testují duplicitní názvy nebo ID nebo jiné podmínky, které můžou způsobit chyby při načítání. |
ValidationCategories | Při ValidateCustom metoda je volána. Ověření v této kategorii lze vyvolat pouze z kódu programu. Další informace naleznete v tématu Vlastní kategorie ověřování. |
Kam umístit metody ověřování
Stejný účinek můžete často dosáhnout umístěním metody ověřování na jiný typ. Můžete například přidat metodu do třídy Person místo vztahu ParentsHaveChildren a iterovat prostřednictvím odkazů:
[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)
{ ...
Agregace omezení ověřování Chcete-li použít ověřování v předvídatelné pořadí, definujte jednu metodu ověření ve třídě vlastníka, jako je kořenový prvek modelu. Tato technika také umožňuje agregovat více zpráv o chybách do jedné zprávy.
Nevýhodou je, že kombinovaná metoda je méně snadná správa a že omezení musí mít stejnou ValidationCategories
. Proto doporučujeme zachovat každé omezení v samostatné metodě, pokud je to možné.
Předávání hodnot v kontextové mezipaměti Kontextový parametr má slovník, do kterého můžete umístit libovolné hodnoty. Slovník přetrvává po dobu životnosti spuštění ověření. Konkrétní metoda ověřování může například zachovat počet chyb v kontextu a použít ji k tomu, aby se zabránilo zahltit okno chyby opakovanými zprávami. Příklad:
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( ... ); }
Ověření násobení měst
Metody ověřování pro kontrolu minimálního násobení se automaticky vygenerují pro váš DSL. Kód se zapíše do souboru Dsl\Generated Code\MultiplicityValidation.cs. Tyto metody se projeví při povolení ověřování v uzlu Editor\Validation v Průzkumníku DSL.
Pokud nastavíte násobnost role vztahu domény na hodnotu 1..* nebo 1..1, ale uživatel nevytvoří propojení této relace, zobrazí se chybová zpráva ověření.
Pokud má například dsl třídy Person and Town a vztah PersonLivesInTown se vztahem 1..\* v roli Město, zobrazí se pro každou osobu, která nemá žádné město, chybová zpráva.
Spuštění ověření z kódu programu
Ověření můžete spustit tak, že k němu přistupujete nebo vytváříte ValidationController. Pokud chcete, aby se chyby zobrazovaly uživateli v okně chyby, použijte ValidationController, který je připojený k DocData diagramu. Pokud například píšete příkaz nabídky, CurrentDocData.ValidationController
je k dispozici ve třídě sady příkazů:
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;
...
Další informace naleznete v tématu Postupy: Přidání příkazu do místní nabídky.
Můžete také vytvořit samostatný kontroler ověření a spravovat chyby sami. Příklad:
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) { ... }
}
Spuštění ověření při výskytu změny
Pokud chcete zajistit, aby se uživatel okamžitě zobrazoval upozornění, pokud se model stane neplatným, můžete definovat událost úložiště, která spouští ověření. Další informace o událostech úložiště naleznete v tématu Obslužné rutiny událostí šíření změn mimo model.
Kromě ověřovacího kódu přidejte do projektu DslPackage vlastní soubor kódu s obsahem podobným následujícímu příkladu. Tento kód používá ValidationController
kód, který je připojen k dokumentu. Tento kontroler zobrazí chyby ověření v seznamu chyb sady 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);
}
}
}
Obslužné rutiny se také nazývají operace Zpět nebo Znovu, které ovlivňují propojení nebo prvky.
Vlastní kategorie ověřování
Kromě standardních kategorií ověřování, jako je Nabídka a Otevřít, můžete definovat i vlastní kategorie. Tyto kategorie můžete vyvolat z kódu programu. Uživatel je nemůže vyvolat přímo.
Typickým použitím vlastních kategorií je definování kategorie, která testuje, jestli model splňuje předpoklady konkrétního nástroje.
Pokud chcete přidat metodu ověřování do konkrétní kategorie, přidejte ji předponu atributem, jako je tento:
[ValidationMethod(CustomCategory = "PreconditionsForGeneratePartsList")]
[ValidationMethod(ValidationCategory.Menu)]
private void TestForCircularLinks(ValidationContext context)
{...}
Poznámka:
Metodu můžete předponovat libovolným počtem [ValidationMethod()]
atributů. Do vlastních i standardních kategorií můžete přidat metodu.
Vyvolání vlastního ověření:
// Invoke all validation methods in a custom category:
validationController.ValidateCustom
(store, // or a list of model elements
"PreconditionsForGeneratePartsList");
Alternativy k ověřování
Omezení ověřování hlásí chyby, ale nemění model. Pokud místo toho chcete zabránit zneplatnění modelu, můžete použít jiné techniky.
Tyto techniky se však nedoporučuje. Obvykle je lepší nechat uživatele rozhodnout, jak opravit neplatný model.
Upravte změnu a obnovte model tak, aby byl platný. Pokud například uživatel nastaví vlastnost nad povoleným maximem, můžete vlastnost obnovit na maximální hodnotu. Chcete-li to provést, definujte pravidlo. Další informace naleznete v tématu Pravidla šíření změn v rámci modelu.
Vrácení transakce zpět, pokud se pokusíte o neplatnou změnu. Můžete také definovat pravidlo pro tento účel, ale v některých případech je možné přepsat obslužnou rutinu vlastnosti OnValueChanging() nebo přepsat metodu, jako OnDeleted().
je například Vrácení transakce zpět, použijte this.Store.TransactionManager.CurrentTransaction.Rollback().
Další informace naleznete v tématu Domain Property Value Change Handlers.
Upozorňující
Ujistěte se, že uživatel ví, že změna byla upravena nebo vrácena zpět. Například použijte System.Windows.Forms.MessageBox.Show("message").