共用方式為


規則傳播模型內的變更

您可以建立存放區規則,以在視覺效果和模型 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();
   }
 }
}

注意

規則的程式碼應該只會變更存放區內元素的狀態;也就是說,規則應該只會變更模型元素、關聯性、圖形、連接器、圖表或其屬性。 如果您要將變更傳播至存放區外部的資源,請定義存放區事件。 如需詳細資訊,請參閱 事件處理常式傳播在模型外的變更

若要定義規則

  1. 將規則定義為前面加上 RuleOn 屬性的類別。 屬性會將規則與其中一個領域類別、關聯性或圖表元素產生關聯。 此規則會套用至這個類別的每個執行個體,這可能是抽象的。

  2. 將規則新增至領域模型類別中傳回的 GetCustomDomainModelTypes() 集合來註冊規則。

  3. 從其中一個抽象規則類別衍生規則類別,並撰寫執行方法的程式碼。

    下列各節會更詳細地說明這些步驟。

若要在領域類別上定義規則

  • 在自訂程式碼檔案中定義類別,並以屬性 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 屬性變更時,就會觸發此規則。

    在許多情況下,覆寫 OnValueChangedOnValueChanging 屬性處理常式會比較方便。 會在變更前後立即呼叫這些方法。 相反地,通常會在交易結束時執行規則。 如需詳細資訊,請參閱領域屬性值變更處理常式注意:建立或刪除連結時,不會觸發此規則。 請改為針對領域關聯寫入 AddRuleDeleteRule
    DeletingRule 在即將刪除元素或連結時觸發。 ModelElement.IsDeleting 屬性為 true,直到交易結束為止。
    DeleteRule 刪除元素或連結時執行。 會在執行所有其他規則之後才執行此規則,包括 DeletingRules。 ModelElement.IsDeleting 為 false,且 ModelElement.IsDeleted 為 true。 若要允許後續的復原,實際上不會從記憶體中移除元素,但會從 Store.ElementDirectory 中移除元素。
    MoveRule 元素會從一個存放區分割區移至另一個分割區。

    (請注意,這與圖形的圖形位置無關。)
    RolePlayerChangeRule 此規則僅適用於領域關聯。 如果您明確將模型元素指派給連結的任一端,就會觸發。
    RolePlayerPositionChangeRule 使用連結上的 MoveBefore 或 MoveToIndex 方法變更元素的順序時,就會觸發。
    TransactionBeginningRule 建立交易時執行。
    TransactionCommittingRule 交易即將認可時執行。
    TransactionRollingBackRule 交易即將復原時執行。
  • 每個類別都有您覆寫的方法。 在您的類別中輸入 override 來加以探索。 這個方法的參數會識別正在變更的元素。

    請注意有關規則的下列重點:

  1. 交易中的變更集可能會觸發許多規則。 通常,認可最外層交易時會執行規則。 規則會以未指定的順序執行。

  2. 規則一律會在交易內執行。 因此,您不必建立新的交易來進行變更。

  3. 復原交易時,或執行復原或重做作業時,不會執行規則。 這些作業會將存放區的所有內容重設為其先前的狀態。 因此,如果您的規則變更存放區以外任何項目的狀態,其可能不會與存放區內容保持同步。 若要更新存放區外部的狀態,最好是使用事件。 如需詳細資訊,請參閱 事件處理常式傳播在模型外的變更

  4. 從檔案載入模型時,會執行某些規則。 若要判斷是否正在進行載入或儲存,請使用 store.TransactionManager.CurrentTransaction.IsSerializing

  5. 如果規則的程式碼建立更多規則觸發程序,則會將其新增至觸發清單的結尾,並在交易完成之前執行。 DeletedRules 會在所有其他規則之後執行。 一個規則可以在交易中執行多次,每次變更執行一次。

  6. 若要在規則中傳入和傳出資訊,您可以將資訊儲存在 TransactionContext 中。 這只是在交易期間維護的字典。 交易結束時就會加以處置。 每個規則中的事件引數都會提供其存取權。 請記住,規則不會以可預測的順序執行。

  7. 考慮其他替代方案之後,請使用規則。 例如,如果您要在值變更時更新屬性,請考慮使用計算屬性。 如果您要限制圖形的大小或位置,請使用 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();
    }
  }

}