Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Как автор языка для конкретного домена (DSL), можно определить ограничения проверки, чтобы убедиться, что модель, созданная пользователем, имеет смысл. Например, если ваш DSL позволяет пользователям рисовать семейное дерево людей и их предков, можно написать ограничение, которое гарантирует, что дети имеют даты рождения после их родителей.
Ограничения проверки могут выполняться, когда модель сохраняется, когда она открыта, и когда пользователь явно запускает команду меню "Проверка ". Вы также можете выполнить проверку под управлением программы. Например, можно выполнить проверку в ответ на изменение значения или связи свойства.
Проверка особенно важна, если вы пишете текстовые шаблоны или другие средства, обрабатывающие модели пользователей. Проверка гарантирует, что модели выполняют предварительные условия, принятые этими инструментами.
Предупреждение
Можно также разрешить определение ограничений проверки в отдельных расширениях для DSL, а также команд меню расширения и обработчиков жестов. Пользователи могут установить эти расширения в дополнение к DSL. Дополнительные сведения см. в разделе "Расширение DSL" с помощью MEF.
Выполнение валидации
Когда пользователь редактирует модель, то есть экземпляр языка, специфичного для домена, следующие действия могут выполнять проверку:
Щелкните схему правой кнопкой мыши и выберите "Проверить все".
Щелкните правой кнопкой мыши верхний узел в обозревателе DSL и выберите "Проверить все"
Сохраните модель.
Откройте модель.
Кроме того, можно написать код программы, который выполняет проверку, например в рамках команды меню или в ответ на изменение.
Все ошибки проверки будут отображаться в окне списка ошибок . Пользователь может дважды щелкнуть сообщение об ошибке, чтобы выбрать элементы модели, которые являются причиной ошибки.
Определение ограничений проверки
Ограничения проверки определяются путем добавления методов проверки в классы домена или связи DSL. Когда проверка запускается либо пользователем, либо под управлением программы, выполняются некоторые или все методы проверки. Каждый метод применяется к каждому экземпляру своего класса, и в каждом классе может быть несколько методов проверки.
Каждый метод проверки сообщает об ошибках, которые он находит.
Замечание
Методы проверки сообщают об ошибках, но не изменяют модель. Если вы хотите изменить или предотвратить определенные изменения, см. статью "Альтернативные варианты проверки".
Определение ограничения проверки
Включите проверку в узле editor\Validation :
Откройте dsl\DslDefinition.dsl.
В обозревателе DSL разверните узел Редактор и выберите Проверка.
В окне "Свойства" задайте свойства Uses как
true. Удобнее всего задать все эти свойства.Щелкните "Преобразовать все шаблоны " на панели инструментов обозревателя решений .
Напишите определения частичного класса для одного или нескольких классов домена или связей домена. Напишите эти определения в новом файле кода в проекте Dsl .
Префикс каждого класса с этим атрибутом:
[ValidationState(ValidationState.Enabled)]- По умолчанию этот атрибут также включает проверку производных классов. Если вы хотите отключить проверку для определенного производного класса, можно использовать
ValidationState.Disabled.
- По умолчанию этот атрибут также включает проверку производных классов. Если вы хотите отключить проверку для определенного производного класса, можно использовать
Добавьте методы проверки в классы. Каждый метод проверки может иметь любое имя, но имеет один параметр типа ValidationContext.
Он должен быть префиксирован одним или несколькими
ValidationMethodатрибутами:[ValidationMethod (ValidationCategories.Open | ValidationCategories.Save | ValidationCategories.Menu ) ]Категории проверки указывают, когда выполняется метод.
Рассмотрим пример.
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.LogErrorLogWarningилиLogInfo.В вызове LogError можно указать список элементов модели или связей, которые будут выбраны, когда пользователь дважды щелкает сообщение об ошибке.
Сведения о том, как считывать модель в коде программы, см. в статье "Навигация и обновление модели в коде программы".
Пример применяется к следующей модели домена. Отношение ParentsHaveChildren имеет роли, которые называются Ребенок и Родитель.
Категории проверки
В атрибуте ValidationMethodAttribute указывается, когда должен выполняться метод проверки.
| Категория | Execution |
|---|---|
| ValidationCategories | Когда пользователь вызывает команду меню "Проверить". |
| ValidationCategories | При открытии файла модели. |
| ValidationCategories | При сохранении файла. Если возникают ошибки проверки, пользователю будет предоставлен параметр отмены операции сохранения. |
| ValidationCategories | При сохранении файла. Если при работе методов этой категории возникают ошибки, пользователю предупреждают о том, что файл может быть невозможно открыть повторно. Используйте эту категорию для методов проверки, которые проверяют наличие повторяющихся имен или идентификаторов или других условий, которые могут привести к ошибкам загрузки. |
| ValidationCategories | При вызове метода ValidateCustom. Проверки в этой категории можно вызывать только из кода программы. Дополнительные сведения см. в разделе "Пользовательские категории проверки". |
Место для размещения методов проверки
Часто можно добиться того же эффекта, поместив метод проверки на другой тип. Например, можно добавить метод в класс Person вместо отношения 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 имеет классы Person и Town, а связь PersonLivesInTown с отношением 1..\* на роли города, то для каждого человека, у которого нет города, появится сообщение об ошибке.
Выполнение проверки из кода программы
Чтобы выполнить проверку, можно получить доступ или создать ValidationController. Если вы хотите, чтобы ошибки отображались пользователю в окне с ошибками, используйте ValidationController, подключенный к DocData схемы. Например, если вы пишете команду меню, 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);
}
}
}
Обработчики также вызываются после операций Отмена или Повтор, затрагивающих ссылки или элементы.
Пользовательские категории валидации
Помимо стандартных категорий проверки, таких как меню и открытие, можно определить собственные категории. Эти категории можно вызвать из кода программы. Пользователь не может вызывать их напрямую.
Обычное использование пользовательских категорий заключается в определении категории, которая проверяет, соответствует ли модель предварительным требованиям определенного инструмента.
Чтобы добавить метод проверки в определенную категорию, префиксировать его атрибутом следующим образом:
[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")..