Etki Alanına Özgü bir Dilde Doğrulama
Etki alanına özgü bir dilin (DSL) yazarı olarak, kullanıcı tarafından oluşturulan modelin anlamlı olduğunu doğrulamak için doğrulama kısıtlamaları tanımlayabilirsiniz. Örneğin, DSL'niz kullanıcıların bir insan ve atalarından oluşan bir aile ağacı çizmesine izin veriyorsa, çocukların ebeveynlerinden sonra doğum tarihleri olmasını sağlayan bir kısıtlama yazabilirsiniz.
Doğrulama kısıtlamalarının model kaydedildiğinde, açıldığında ve kullanıcı açıkça Doğrula menü komutunu çalıştırdığında yürütmesini sağlayabilirsiniz. Doğrulamayı program denetimi altında da yürütebilirsiniz. Örneğin, bir özellik değeri veya ilişkisindeki değişikliğe yanıt olarak doğrulama yürütebilirsiniz.
Kullanıcılarınızın modellerini işleyen metin şablonları veya diğer araçlar yazıyorsanız doğrulama özellikle önemlidir. Doğrulama, modellerin bu araçlar tarafından kabul edilen önkoşulları yerine getirmesini sağlar.
Uyarı
Ayrıca, uzantı menüsü komutları ve hareket işleyicileriyle birlikte DSL'nizin ayrı uzantılarında doğrulama kısıtlamalarının tanımlanmasına izin vekleyebilirsiniz. Kullanıcılar DSL'nize ek olarak bu uzantıları yüklemeyi seçebilir. Daha fazla bilgi için bkz . MEF kullanarak DSL'nizi genişletme.
DoğrulamaYı Çalıştırma
Kullanıcı bir modeli, yani etki alanına özgü dilinizin bir örneğini düzenlerken, aşağıdaki eylemler doğrulamayı çalıştırabilir:
Diyagrama sağ tıklayın ve Tümünü Doğrula'yı seçin.
DSL'nizin Gezgini'nde üst düğüme sağ tıklayın ve Tümünü Doğrula'yı seçin
Modeli kaydedin.
Modeli açın.
Buna ek olarak, doğrulamayı çalıştıran program kodu yazabilirsiniz; örneğin, bir menü komutunun parçası olarak veya bir değişikliğe yanıt olarak.
Tüm doğrulama hataları Hata Listesi penceresinde görüntülenir. Kullanıcı hatanın nedeni olan model öğelerini seçmek için bir hata iletisine çift tıklayabilir.
Doğrulama Kısıtlamalarını Tanımlama
DSL'nizin etki alanı sınıflarına veya ilişkilerine doğrulama yöntemleri ekleyerek doğrulama kısıtlamaları tanımlarsınız. Doğrulama kullanıcı tarafından veya program denetimi altında çalıştırıldığında doğrulama yöntemlerinin bir kısmı veya tümü yürütülür. Her yöntem kendi sınıfının her örneğine uygulanır ve her sınıfta birkaç doğrulama yöntemi olabilir.
Her doğrulama yöntemi bulduğu hataları bildirir.
Not
Doğrulama yöntemleri hataları bildirir, ancak modeli değiştirmez. Belirli değişiklikleri ayarlamak veya önlemek istiyorsanız bkz . Doğrulama alternatifleri.
Doğrulama kısıtlaması tanımlamak için
Düzenleyici\Doğrulama düğümünde doğrulamayı etkinleştirin:
Dsl\DslDefinition.dsl dosyasını açın.
DSL Gezgini'nde Düzenleyici düğümünü genişletin ve Doğrulama'yı seçin.
Özellikler penceresi, Kullanım özellikleri'ni olarak
true
ayarlayın. Bu özelliklerin tümünü ayarlamak en uygun seçenektir.Çözüm Gezgini araç çubuğunda Tüm Şablonları Dönüştür'e tıklayın.
Bir veya daha fazla etki alanı sınıfınız veya etki alanı ilişkileriniz için kısmi sınıf tanımları yazın. Bu tanımları Dsl projesindeki yeni bir kod dosyasına yazın.
Her sınıfa şu öznitelikle ön ek ekleyin:
[ValidationState(ValidationState.Enabled)]
- Varsayılan olarak, bu öznitelik türetilmiş sınıflar için doğrulamayı da etkinleştirir. Belirli bir türetilmiş sınıf için doğrulamayı devre dışı bırakmak istiyorsanız kullanabilirsiniz
ValidationState.Disabled
.
- Varsayılan olarak, bu öznitelik türetilmiş sınıflar için doğrulamayı da etkinleştirir. Belirli bir türetilmiş sınıf için doğrulamayı devre dışı bırakmak istiyorsanız kullanabilirsiniz
Sınıflara doğrulama yöntemleri ekleyin. Her doğrulama yönteminin herhangi bir adı olabilir, ancak türünde ValidationContextbir parametresi vardır.
Bir veya daha fazla
ValidationMethod
öznitelik ön ekiyle eklenmelidir:[ValidationMethod (ValidationCategories.Open | ValidationCategories.Save | ValidationCategories.Menu ) ]
ValidationCategories yöntemin ne zaman yürütüleceğini belirtir.
Örneğin:
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);
}
}
Bu kodla ilgili aşağıdaki noktalara dikkat edin:
Etki alanı sınıflarına veya etki alanı ilişkilerine doğrulama yöntemleri ekleyebilirsiniz. Bu türlerin kodu Dsl\Generated Code\Domain*.cs biçimindedir.
Her doğrulama yöntemi, sınıfının ve alt sınıflarının her örneğine uygulanır. Etki alanı ilişkisi söz konusu olduğunda, her örnek iki model öğesi arasındaki bir bağlantıdır.
Doğrulama yöntemleri belirtilen herhangi bir sırada uygulanmaz ve her yöntem sınıfının örneklerine öngörülebilir bir sırada uygulanmaz.
Bu tutarsız sonuçlara yol açacağından, bir doğrulama yönteminin mağaza içeriğini güncelleştirmesi genellikle kötü bir uygulamadır. Bunun yerine, yönteminin veya çağırarak
context.LogError
LogWarning
LogInfo
herhangi bir hata bildirmesi gerekir.LogError çağrısında, kullanıcı hata iletisine çift tıkladığında seçilecek model öğelerinin veya ilişki bağlantılarının listesini sağlayabilirsiniz.
Program kodunda modelin nasıl okunduğu hakkında bilgi için bkz . Program Kodunda Model Gezinme ve Güncelleştirme.
Örnek aşağıdaki etki alanı modeli için geçerlidir. ParentsHaveChildren ilişkisinin Child ve Parent adlı rolleri vardır.
Doğrulama Kategorileri
özniteliğinde ValidationMethodAttribute doğrulama yönteminin ne zaman yürütülmesi gerektiğini belirtirsiniz.
Kategori | Yürütme |
---|---|
ValidationCategories | Kullanıcı Doğrula menü komutunu çağırdığında. |
ValidationCategories | Model dosyası açıldığında. |
ValidationCategories | Dosya kaydedildiğinde. Doğrulama hataları varsa kullanıcıya kaydetme işlemini iptal etme seçeneği verilir. |
ValidationCategories | Dosya kaydedildiğinde. Bu kategorideki yöntemlerden hatalar varsa, kullanıcı dosyayı yeniden açmanın mümkün olmayabileceği konusunda uyarılır. Yinelenen adları veya kimlikleri veya yükleme hatalarına neden olabilecek diğer koşulları test eden doğrulama yöntemleri için bu kategoriyi kullanın. |
ValidationCategories | ValidateCustom yöntemi çağrıldığında. Bu kategorideki doğrulamalar yalnızca program kodundan çağrılabilir. Daha fazla bilgi için bkz . Özel Doğrulama Kategorileri. |
Doğrulama Yöntemlerinin Yerleştirileceği Yer
Genellikle farklı bir türe doğrulama yöntemi yerleştirerek aynı etkiyi elde edebilirsiniz. Örneğin, ParentsHaveChildren ilişkisi yerine Person sınıfına bir yöntem ekleyebilir ve bağlantıları yineleyebilirsiniz:
[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)
{ ...
Doğrulama kısıtlamaları toplanıyor. Doğrulamayı tahmin edilebilir bir sırada uygulamak için, modelinizin kök öğesi gibi bir sahip sınıfında tek bir doğrulama yöntemi tanımlayın. Bu teknik, birden çok hata raporunu tek bir iletide toplamanıza da olanak tanır.
Dezavantajları, birleştirilmiş yöntemin yönetilmesinin daha az kolay olması ve kısıtlamaların tümünün aynı ValidationCategories
olması gerektiğidir. Bu nedenle mümkünse her kısıtlamayı ayrı bir yöntemde tutmanızı öneririz.
Bağlam önbelleğindeki değerleri geçirme. Bağlam parametresi, rastgele değerler yerleştirebileceğiniz bir sözlüğe sahiptir. Sözlük, doğrulama çalıştırmasının ömrü boyunca devam eder. Örneğin belirli bir doğrulama yöntemi, bağlamda bir hata sayısı tutabilir ve yinelenen iletilerle hata penceresinin taşmasını önlemek için bunu kullanabilir. Örneğin:
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( ... ); }
Çarpmaların Doğrulanması
DSL'niz için minimum çokluğu denetlemeye yönelik doğrulama yöntemleri otomatik olarak oluşturulur. Kod Dsl\Generated Code\MultiplicityValidation.cs dosyasına yazılır. Bu yöntemler, DSL Gezgini'ndeki Editor\Validation düğümünde doğrulamayı etkinleştirdiğinizde geçerlilik kazanır.
Bir etki alanı ilişkisinin rolünün çokluğunu 1..*veya 1..1 olarak ayarlarsanız, ancak kullanıcı bu ilişkinin bağlantısını oluşturmazsa, doğrulama hata iletisi görüntülenir.
Örneğin, DSL'nizde Kişi ve Kasaba sınıfları ve Şehir rolünde 1..\* ilişkisine sahip bir PersonLivesInTown ilişkisi varsa, Şehir olmayan her Kişi için bir hata iletisi görüntülenir.
Program Kodundan DoğrulamaYı Çalıştırma
ValidationController'a erişerek veya oluşturarak doğrulamayı çalıştırabilirsiniz. Hataların kullanıcıya hata penceresinde görüntülenmesini istiyorsanız, diyagramınızın DocData'sına bağlı ValidationController'ı kullanın. Örneğin, bir menü komutu yazıyorsanız, CurrentDocData.ValidationController
komut kümesi sınıfında kullanılabilir:
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;
...
Daha fazla bilgi için bkz . Nasıl yapılır: Kısayol Menüsüne Komut Ekleme.
Ayrıca ayrı bir doğrulama denetleyicisi oluşturabilir ve hataları kendiniz yönetebilirsiniz. Örneğin:
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) { ... }
}
Değişiklik olduğunda doğrulama çalıştırılıyor
Model geçersiz hale gelirse kullanıcının hemen uyarıldığından emin olmak istiyorsanız doğrulamayı çalıştıran bir depolama olayı tanımlayabilirsiniz. Depolama olayları hakkında daha fazla bilgi için bkz . Olay İşleyicileri Değişiklikleri Modelin Dışına Yayıyor.
Doğrulama koduna ek olarak DslPackage projenize aşağıdaki örneğe benzer içeriğe sahip özel bir kod dosyası ekleyin. Bu kod, ValidationController
belgeye eklenmiş olan öğesini kullanır. Bu denetleyici, Visual Studio hata listesinde doğrulama hatalarını görüntüler.
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);
}
}
}
İşleyiciler, bağlantıları veya öğeleri etkileyen Geri Al veya Yinele işlemlerinden sonra da çağrılır.
Özel Doğrulama Kategorileri
Menü ve Aç gibi standart doğrulama kategorilerine ek olarak kendi kategorilerinizi de tanımlayabilirsiniz. Bu kategorileri program kodundan çağırabilirsiniz. Kullanıcı bunları doğrudan çağıramaz.
Özel kategoriler için tipik bir kullanım, modelin belirli bir aracın önkoşullarını karşılayıp karşılamadığını test eden bir kategori tanımlamaktır.
Belirli bir kategoriye doğrulama yöntemi eklemek için, bunun ön ekine aşağıdaki gibi bir öznitelik ekleyin:
[ValidationMethod(CustomCategory = "PreconditionsForGeneratePartsList")]
[ValidationMethod(ValidationCategory.Menu)]
private void TestForCircularLinks(ValidationContext context)
{...}
Not
İstediğiniz sayıda [ValidationMethod()]
özniteliği olan bir yöntemin ön eklerini oluşturabilirsiniz. Hem özel hem de standart kategorilere yöntem ekleyebilirsiniz.
Özel doğrulamayı çağırmak için:
// Invoke all validation methods in a custom category:
validationController.ValidateCustom
(store, // or a list of model elements
"PreconditionsForGeneratePartsList");
Doğrulamanın Alternatifleri
Doğrulama kısıtlamaları hataları bildirir, ancak modeli değiştirmez. Bunun yerine modelin geçersiz olmasını önlemek istiyorsanız, diğer teknikleri kullanabilirsiniz.
Ancak bu teknikler önerilmez. Geçersiz bir modelin nasıl düzelteceğine kullanıcının karar vermesine izin vermek genellikle daha iyidir.
Modeli geçerliliğe geri yüklemek için değişikliği ayarlayın. Örneğin, kullanıcı izin verilen üst sınırın üzerinde bir özellik ayarlarsa, özelliği en yüksek değere sıfırlayabilirsiniz. Bunu yapmak için bir kural tanımlayın. Daha fazla bilgi için bkz . Kurallar Model İçinde Değişiklikleri Yayma.
Geçersiz bir değişiklik denenirse işlemi geri alın. Bu amaçla bir kural da tanımlayabilirsiniz, ancak bazı durumlarda OnValueChanging() özellik işleyicisini geçersiz kılmak veya bir işlemi geri almak için komutunu kullanmak this.Store.TransactionManager.CurrentTransaction.Rollback().
gibi bir yöntemi OnDeleted().
geçersiz kılmak mümkündür. Daha fazla bilgi için bkz. Etki Alanı Özellik Değeri Değişiklik İşleyicileri.
Uyarı
Kullanıcının değişikliğin ayarlandığını veya geri getirildiğini bildiğinden emin olun. Örneğin, System.Windows.Forms.MessageBox.Show("message").