規則傳播模型內的變更
您可以建立存放區規則,以在視覺效果和模型 SDK (VMSDK) 中將變更從某個元素傳播到另一個元素。 當變更發生在存放區中的任何元素時,會排程執行規則,通常是在認可最外部的交易時。 不同類型的事件有不同類型的規則,例如新增元素或刪除元素。 您可以將規則附加至特定類型的元素、圖形或圖表。 許多內建功能是由規則所定義:例如,規則可確保模型變更時會更新圖表。 您可以新增自己的規則來自訂特定領域語言。
存放區規則特別適用於在存放區內傳播變更,也就是模型元素、關聯性、圖形或連接器及其網域屬性的變更。 當使用者叫用復原或取消復原命令時,規則並不會執行。 相反地,交易管理員會確定存放區內容已還原為正確的狀態。 如果您要將變更傳播至存放區外部的資源,請使用存放區事件。 如需詳細資訊,請參閱 事件處理常式傳播在模型外的變更。
例如,假設您要指定每當使用者 (或程式碼) 建立 ExampleDomainClass 類型的新元素時,就會在模型的另一個部分中建立另一個類型的其他元素。 您可以撰寫 AddRule,並將其與 ExampleDomainClass 產生關聯。 您會在規則中撰寫程式碼,以建立其他元素。
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();
}
}
}
注意
規則的程式碼應該只會變更存放區內元素的狀態;也就是說,規則應該只會變更模型元素、關聯性、圖形、連接器、圖表或其屬性。 如果您要將變更傳播至存放區外部的資源,請定義存放區事件。 如需詳細資訊,請參閱 事件處理常式傳播在模型外的變更。
若要定義規則
將規則定義為前面加上
RuleOn
屬性的類別。 屬性會將規則與其中一個領域類別、關聯性或圖表元素產生關聯。 此規則會套用至這個類別的每個執行個體,這可能是抽象的。將規則新增至領域模型類別中傳回的
GetCustomDomainModelTypes()
集合來註冊規則。從其中一個抽象規則類別衍生規則類別,並撰寫執行方法的程式碼。
下列各節會更詳細地說明這些步驟。
若要在領域類別上定義規則
在自訂程式碼檔案中定義類別,並以屬性 RuleOnAttribute 做為前置詞:
[RuleOn(typeof(ExampleElement), // Usual value - but required, because it is not the default: FireTime = TimeToFire.TopLevelCommit)] class MyRule ...
第一個參數中的主旨類型可以是領域類別、領域關聯性、圖形、連接器或圖表。 通常,您會將規則套用至領域類別和關聯性。
FireTime
通常是TopLevelCommit
。 這可確保只有在交易的所有主要變更完成之後,才會執行規則。 替代方法是 Inline,會在變更後不久執行規則;以及 LocalCommit,其會在目前交易的結尾 (可能不是最外層) 執行規則。 您也可以設定規則的優先順序,以影響其在佇列中的排序,但這對於達成所需結果並不是可靠的方法。您可以將抽象類別指定為主體類型。
此規則會套用至主旨類別的所有執行個體。
FireTime
的預設值為 TimeToFire.TopLevelCommit。 這會導致認可最外層交易時執行規則。 替代方法是 TimeToFire.Inline。 這會導致觸發事件之後不久執行規則。
若要註冊規則
將規則類別新增至
GetCustomDomainModelTypes
在領域模型中所傳回的類型清單: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(); } }
如果您不確定領域模型類別的名稱,請查看 Dsl\GeneratedCode\DomainModel.cs 檔案
在 DSL 專案的自訂程式碼檔案中撰寫這個程式碼。
若要撰寫規則的程式碼
從下列其中一個基底類別衍生規則類別:
基底類別 觸發程序 AddRule 新增元素、連結或圖形。
除了新的元素之外,使用此方法來偵測新的關聯性。ChangeRule 領域屬性值已變更。 方法引數會提供舊值和新值。
針對圖形,如果移動圖形,則當內建AbsoluteBounds
屬性變更時,就會觸發此規則。
在許多情況下,覆寫OnValueChanged
或OnValueChanging
屬性處理常式會比較方便。 會在變更前後立即呼叫這些方法。 相反地,通常會在交易結束時執行規則。 如需詳細資訊,請參閱領域屬性值變更處理常式。 注意:建立或刪除連結時,不會觸發此規則。 請改為針對領域關聯寫入AddRule
和DeleteRule
。DeletingRule 在即將刪除元素或連結時觸發。 ModelElement.IsDeleting 屬性為 true,直到交易結束為止。 DeleteRule 刪除元素或連結時執行。 會在執行所有其他規則之後才執行此規則,包括 DeletingRules。 ModelElement.IsDeleting 為 false,且 ModelElement.IsDeleted 為 true。 若要允許後續的復原,實際上不會從記憶體中移除元素,但會從 Store.ElementDirectory 中移除元素。 MoveRule 元素會從一個存放區分割區移至另一個分割區。
(請注意,這與圖形的圖形位置無關。)RolePlayerChangeRule 此規則僅適用於領域關聯。 如果您明確將模型元素指派給連結的任一端,就會觸發。 RolePlayerPositionChangeRule 使用連結上的 MoveBefore 或 MoveToIndex 方法變更元素的順序時,就會觸發。 TransactionBeginningRule 建立交易時執行。 TransactionCommittingRule 交易即將認可時執行。 TransactionRollingBackRule 交易即將復原時執行。 每個類別都有您覆寫的方法。 在您的類別中輸入
override
來加以探索。 這個方法的參數會識別正在變更的元素。請注意有關規則的下列重點:
交易中的變更集可能會觸發許多規則。 通常,認可最外層交易時會執行規則。 規則會以未指定的順序執行。
規則一律會在交易內執行。 因此,您不必建立新的交易來進行變更。
復原交易時,或執行復原或重做作業時,不會執行規則。 這些作業會將存放區的所有內容重設為其先前的狀態。 因此,如果您的規則變更存放區以外任何項目的狀態,其可能不會與存放區內容保持同步。 若要更新存放區外部的狀態,最好是使用事件。 如需詳細資訊,請參閱 事件處理常式傳播在模型外的變更。
從檔案載入模型時,會執行某些規則。 若要判斷是否正在進行載入或儲存,請使用
store.TransactionManager.CurrentTransaction.IsSerializing
。如果規則的程式碼建立更多規則觸發程序,則會將其新增至觸發清單的結尾,並在交易完成之前執行。 DeletedRules 會在所有其他規則之後執行。 一個規則可以在交易中執行多次,每次變更執行一次。
若要在規則中傳入和傳出資訊,您可以將資訊儲存在
TransactionContext
中。 這只是在交易期間維護的字典。 交易結束時就會加以處置。 每個規則中的事件引數都會提供其存取權。 請記住,規則不會以可預測的順序執行。考慮其他替代方案之後,請使用規則。 例如,如果您要在值變更時更新屬性,請考慮使用計算屬性。 如果您要限制圖形的大小或位置,請使用
BoundsRule
。 如果您要回應屬性值中的變更,請將OnValueChanged
處理常式新增至屬性。 如需詳細資訊,請參閱回應及傳播變更。
範例
下列範例會在具現化領域關聯以連結兩個元素時更新屬性。 此規則不僅會在使用者於圖表上建立連結時觸發,還會在程式碼建立連結時觸發。
若要測試此範例,請使用 Task Flow 方案範本建立 DSL,並在 Dsl 專案的檔案中插入下列程式碼。 建置並執行方案,然後在 [偵錯] 專案中開啟 [範例] 檔案。 在註解圖形與流程元素之間繪製註解連結。 註解中的文字會變更為報告您與其連線的最新元素。
實際上,您通常會為每個 AddRule 撰寫 DeleteRule。
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();
}
}
}