Reguły propagujące zmiany w modelu
Regułę magazynu można utworzyć, aby propagować zmianę z jednego elementu do innego w zestawie SDK wizualizacji i modelowania (VMSDK). Gdy nastąpi zmiana dowolnego elementu w Sklepie, reguły mają być wykonywane, zwykle w przypadku zatwierdzenia najbardziej zewnętrznej transakcji. Istnieją różne typy reguł dla różnych rodzajów zdarzeń, takich jak dodawanie elementu lub usuwanie go. Reguły można dołączać do określonych typów elementów, kształtów lub diagramów. Wiele wbudowanych funkcji jest definiowanych przez reguły: na przykład reguły zapewniają, że diagram jest aktualizowany po zmianie modelu. Język specyficzny dla domeny można dostosować, dodając własne reguły.
Reguły przechowywania są szczególnie przydatne do propagowania zmian w magazynie — czyli zmian w elementach modelu, relacjach, kształtach lub łącznikach oraz ich właściwościach domeny. Reguły nie są uruchamiane, gdy użytkownik wywołuje polecenia Cofnij lub Wykonaj ponownie. Zamiast tego menedżer transakcji upewnia się, że zawartość magazynu jest przywracana do prawidłowego stanu. Jeśli chcesz propagować zmiany do zasobów spoza magazynu, użyj zdarzeń sklepu. Aby uzyskać więcej informacji, zobacz Programy obsługi zdarzeń propagują zmiany poza modelem.
Załóżmy na przykład, że chcesz określić, że za każdym razem, gdy użytkownik (lub twój kod) tworzy nowy element typu ExampleDomainClass, dodatkowy element innego typu jest tworzony w innej części modelu. Możesz napisać element AddRule i skojarzyć go z exampleDomainClass. Możesz napisać kod w regule, aby utworzyć dodatkowy element.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.Modeling;
namespace ExampleNamespace
{
// Attribute associates the rule with a domain class:
[RuleOn(typeof(ExampleDomainClass), FireTime=TimeToFire.TopLevelCommit)]
// The rule is a class derived from one of the abstract rules:
class MyAddRule : AddRule
{
// Override the abstract method:
public override void ElementAdded(ElementAddedEventArgs e)
{
base.ElementAdded(e);
ExampleDomainClass element = e.ModelElement;
Store store = element.Store;
// Ignore this call if we're currently loading a model:
if (store.TransactionManager.CurrentTransaction.IsSerializing)
return;
// Code here propagates change as required - for example:
AnotherDomainClass echo = new AnotherDomainClass(element.Partition);
echo.Name = element.Name;
echo.Parent = element.Parent;
}
}
// The rule must be registered:
public partial class ExampleDomainModel
{
protected override Type[] GetCustomDomainModelTypes()
{
List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
types.Add(typeof(MyAddRule));
// If you add more rules, list them here.
return types.ToArray();
}
}
}
Uwaga
Kod reguły powinien zmienić stan tylko elementów wewnątrz sklepu; oznacza to, że reguła powinna zmieniać tylko elementy modelu, relacje, kształty, łączniki, diagramy lub ich właściwości. Jeśli chcesz propagować zmiany do zasobów spoza magazynu, zdefiniuj zdarzenia sklepu. Aby uzyskać więcej informacji, zobacz Programy obsługi zdarzeń propagują zmiany poza modelem.
Aby zdefiniować regułę
Zdefiniuj regułę jako klasę poprzedzoną atrybutem
RuleOn
. Atrybut kojarzy regułę z jedną z klas domeny, relacji lub elementów diagramu. Reguła zostanie zastosowana do każdego wystąpienia tej klasy, co może być abstrakcyjne.Zarejestruj regułę, dodając ją do zestawu zwróconego przez
GetCustomDomainModelTypes()
klasę modelu domeny.Utwórz klasę reguł z jednej z abstrakcyjnych klas Reguł i napisz kod metody wykonywania.
W poniższych sekcjach opisano te kroki bardziej szczegółowo.
Aby zdefiniować regułę dla klasy domeny
W pliku kodu niestandardowego zdefiniuj klasę i prefiks za pomocą atrybutu RuleOnAttribute :
[RuleOn(typeof(ExampleElement), // Usual value - but required, because it is not the default: FireTime = TimeToFire.TopLevelCommit)] class MyRule ...
Typ tematu w pierwszym parametrze może być klasą domeny, relacją domeny, kształtem, łącznikiem lub diagramem. Zwykle reguły są stosowane do klas domen i relacji.
Wartość
FireTime
to zwykleTopLevelCommit
. Dzięki temu reguła jest wykonywana dopiero po wprowadzeniu wszystkich podstawowych zmian transakcji. Alternatywy są wbudowane, co powoduje wykonanie reguły wkrótce po zmianie; i LocalCommit, który wykonuje regułę na końcu bieżącej transakcji (co może nie być najbardziej zewnętrzne). Można również ustawić priorytet reguły, aby wpłynąć na jej kolejność w kolejce, ale jest to zawodna metoda osiągnięcia wymaganego wyniku.Klasę abstrakcyjną można określić jako typ podmiotu.
Reguła ma zastosowanie do wszystkich wystąpień klasy podmiotu.
Wartość domyślna to
FireTime
TimeToFire.TopLevelCommit. Powoduje to wykonanie reguły, gdy transakcja najbardziej zewnętrzna zostanie zatwierdzona. Alternatywą jest TimeToFire.Inline. Spowoduje to wykonanie reguły wkrótce po zdarzeniu wyzwalającym.
Aby zarejestrować regułę
Dodaj klasę reguł do listy typów zwracanych przez
GetCustomDomainModelTypes
w modelu domeny:public partial class ExampleDomainModel { protected override Type[] GetCustomDomainModelTypes() { List<Type> types = new List<Type>(base.GetCustomDomainModelTypes()); types.Add(typeof(MyAddRule)); // If you add more rules, list them here. return types.ToArray(); } }
Jeśli nie masz pewności co do nazwy klasy modelu domeny, poszukaj w pliku Dsl\GeneratedCode\DomainModel.cs
Napisz ten kod w niestandardowym pliku kodu w projekcie DSL.
Aby napisać kod reguły
Utwórz pochodną klasę reguł z jednej z następujących klas bazowych:
Klasa bazowa Wyzwalacz AddRule Dodawany jest element, link lub kształt.
Służy do wykrywania nowych relacji oprócz nowych elementów.ChangeRule Wartość właściwości domeny została zmieniona. Argument metody zawiera stare i nowe wartości.
W przypadku kształtów ta reguła jest wyzwalana po zmianie właściwości wbudowanejAbsoluteBounds
, jeśli kształt zostanie przeniesiony.
W wielu przypadkach wygodniejsze jest przesłonięciaOnValueChanged
lubOnValueChanging
w procedurze obsługi właściwości. Te metody są wywoływane bezpośrednio przed i po zmianie. Natomiast reguła zwykle jest uruchamiana na końcu transakcji. Aby uzyskać więcej informacji, zobacz Procedury obsługi zmiany wartości właściwości domeny. Uwaga: ta reguła nie jest wyzwalana po utworzeniu lub usunięciu łącza. Zamiast tego napisz elementAddRule
i dlaDeleteRule
relacji domeny.DeletingRule Wyzwalane po usunięciu elementu lub łącza. Właściwość ModelElement.IsDeleting ma wartość true do końca transakcji. DeleteRule Wykonywane po usunięciu elementu lub łącza. Reguła jest wykonywana po wykonaniu wszystkich innych reguł, w tym usuwania reguł. ModelElement.IsDeleting ma wartość false, a element ModelElement.IsDeleted ma wartość true. Aby umożliwić kolejne cofanie, element nie jest w rzeczywistości usuwany z pamięci, ale jest usuwany z store.ElementDirectory. MoveRule Element jest przenoszony z jednej partycji magazynu do innej.
(Zwróć uwagę, że nie jest to związane z położeniem graficznym kształtu).RolePlayerChangeRule Ta reguła ma zastosowanie tylko do relacji domeny. Zostanie on wyzwolony, jeśli jawnie przypiszesz element modelu na dowolny koniec linku. RolePlayerPositionChangeRule Wyzwalane, gdy kolejność łączy do lub z elementu jest zmieniana przy użyciu metod MoveBefore lub MoveToIndex w linku. TransactionBeginningRule Wykonywane po utworzeniu transakcji. TransactionCommittingRule Wykonywane, gdy transakcja ma zostać zatwierdzona. TransactionRollingBackRule Wykonywane, gdy transakcja ma zostać wycofana. Każda klasa ma metodę zastępowania. Wpisz
override
w klasie, aby ją odnaleźć. Parametr tej metody identyfikuje element, który jest zmieniany.Zwróć uwagę na następujące kwestie dotyczące reguł:
Zestaw zmian w transakcji może wyzwolić wiele reguł. Zwykle reguły są wykonywane po zatwierdzeniu najbardziej zewnętrznej transakcji. Są one wykonywane w nieokreślonej kolejności.
Reguła jest zawsze wykonywana wewnątrz transakcji. W związku z tym nie trzeba tworzyć nowej transakcji w celu wprowadzania zmian.
Reguły nie są wykonywane, gdy transakcja zostanie wycofana lub gdy są wykonywane operacje Cofnij lub Wykonaj ponownie. Te operacje resetuje całą zawartość sklepu do poprzedniego stanu. W związku z tym jeśli reguła zmienia stan niczego poza sklepem, synchronizacja może nie być zsynchronizowana z zawartością Sklepu. Aby zaktualizować stan poza magazynem, lepiej użyć zdarzeń. Aby uzyskać więcej informacji, zobacz Programy obsługi zdarzeń propagują zmiany poza modelem.
Niektóre reguły są wykonywane, gdy model jest ładowany z pliku. Aby określić, czy ładowanie lub zapisywanie jest w toku, użyj polecenia
store.TransactionManager.CurrentTransaction.IsSerializing
.Jeśli kod reguły tworzy więcej wyzwalaczy reguły, zostaną one dodane na końcu listy wypalania i zostaną wykonane przed zakończeniem transakcji. Instrukcje DeletedRules są wykonywane po wszystkich innych regułach. Jedna reguła może być uruchamiana wiele razy w transakcji, raz dla każdej zmiany.
Aby przekazać informacje do i z reguł, możesz przechowywać informacje w pliku
TransactionContext
. Jest to tylko słownik, który jest utrzymywany podczas transakcji. Jest on usuwany po zakończeniu transakcji. Argumenty zdarzeń w każdej regule zapewniają dostęp do niego. Pamiętaj, że reguły nie są wykonywane w przewidywalnej kolejności.Użyj reguł po rozważeniu innych alternatyw. Jeśli na przykład chcesz zaktualizować właściwość po zmianie wartości, rozważ użycie właściwości obliczeniowej. Jeśli chcesz ograniczyć rozmiar lub lokalizację kształtu, użyj elementu
BoundsRule
. Jeśli chcesz odpowiedzieć na zmianę wartości właściwości, dodajOnValueChanged
procedurę obsługi do właściwości . Aby uzyskać więcej informacji, zobacz Odpowiadanie na zmiany i propagowanie ich.
Przykład
Poniższy przykład aktualizuje właściwość po utworzeniu wystąpienia relacji domeny w celu połączenia dwóch elementów. Reguła zostanie wyzwolona nie tylko wtedy, gdy użytkownik utworzy link na diagramie, ale także jeśli kod programu utworzy łącze.
Aby przetestować ten przykład, utwórz rozszerzenie DSL przy użyciu szablonu rozwiązania Przepływu zadań i wstaw następujący kod w pliku w projekcie Dsl. Skompiluj i uruchom rozwiązanie, a następnie otwórz przykładowy plik w projekcie Debugowanie. Rysuj łącze komentarza między kształtem komentarza a elementem przepływu. Tekst w komentarzu zmienia się w raporcie na najnowszy element, z którym został połączony.
W praktyce zwykle należy napisać metodę DeleteRule dla każdego elementu AddRule.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Modeling;
namespace Company.TaskRuleExample
{
[RuleOn(typeof(CommentReferencesSubjects))]
public class RoleRule : AddRule
{
public override void ElementAdded(ElementAddedEventArgs e)
{
base.ElementAdded(e);
CommentReferencesSubjects link = e.ModelElement as CommentReferencesSubjects;
Comment comment = link.Comment;
FlowElement subject = link.Subject;
Transaction current = link.Store.TransactionManager.CurrentTransaction;
// Don't want to run when we're just loading from file:
if (current.IsSerializing) return;
comment.Text = "Flow has " + subject.FlowTo.Count + " outgoing connections";
}
}
public partial class TaskRuleExampleDomainModel
{
protected override Type[] GetCustomDomainModelTypes()
{
List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
types.Add(typeof(RoleRule));
return types.ToArray();
}
}
}