Visual Studio DSL 入門 12---狀態機設計器的規則(Rule)和驗證(Validation)
上一節我們為狀態機設計器添加了一個Rule,主要用來處理當Transition的屬性Label,Condition,Action,Event之間的任何一個值發生變化時,其余的屬性值也要按照我們的規則來更新(我們的Label屬性就是一個輔助的屬性,用來更好的顯示和編輯另外三個屬性).我們可以看到vs.net dsl提供的Rule機制的強大,它主要提供了以下幾個Rule:
AddRule: 當ModelElement或者ElementLink添加時觸發
ChangeRule: 當一個元素或者關系的屬性發生變化時觸發
DeleteingRule: 刪除元素或關系時觸發
DeletedRule: 刪除元素或關系后觸發
RolePlayerChangeRule: 當域關系的一端發生變化時
RolePlayerPositionChangeRule: 對于多重的關系中的角色發生變化
TransactionBeginningRule: 事務開始時觸發
TransactionCommitingRule 事務提交時觸發
TransactionRollingBackRule 事務回滾時觸發
另外應該注意的是,AddRule,ChangeRule,DeleteingRule…這些都是在元素添加,更改,刪除同時觸發,此時還在事務當中,也就是說,我們可以添加自己的規則,根據我們自定義的條件取消事務或做一些其它的處理。
但是規則是強制性的,也就是說,在一個規則處理里面,我們如果限制一個屬性值的類型必須是整型,否則就拋出異常,停止此事務的提交。這屬于Vs.net Dsl提供的硬約束的一種實現,相反,還有軟約束,那硬約束和軟約束有什么不同呢?
硬約束就是指從不讓用戶違反的約束,比如我們例子中的四個屬性之間的這種關系,如果有些個案,就會導致我們的元數據混亂,生成代碼就很麻煩.
軟約束是用戶有時可以違反,有時又不能違反的約束,或者是說,即使用戶違反了,我們也要保證元數據能夠正常保存,正常提交。比如說我們的狀態機中沒有初始狀態.
一個優秀的Dsl設計器應該是硬約束和軟約束結合,軟的不行來硬的! 當然,這里提到Rule只是硬約束的一種,比如我們還可以重載指定域屬性值屬性處理器內嵌類中的OnXXXChanged()方法,例如,我們添加一個partial類ConditionPropertyHandler:
-
internal sealed partial class ConditionPropertyHandler : DomainPropertyValueHandler<Transition, string>
-
{ -
protected override void OnValueChanging(Transition element, string oldValue, string newValue)
-
{ -
if (!element.Store.InUndoRedoOrRollback) -
{ -
if (!string.IsNullOrEmpty(newValue))
-
{ -
element.Label = ComputeSummary(newValue, element.Condition, element.Action);
-
}
-
}
-
base.OnValueChanging(element, oldValue, newValue); -
}
-
}
介紹了硬約束后,我們來看一下vs.net dsl的軟約束的機制:
和硬約束一樣,軟約束也是通過附加的C#類來完成,相比于定義特殊的規則或者是驗證語言來說,這很方便,有了更大的靈活性。 我們來看一下validation機制,這就是vs.net dsl提供的軟約束,它和rule最明顯的區別就是,rule是被動觸發的,當我們操作元數據,對模型進行操作時,觸發了我們定義的Rule(規則),而validation(驗證)一般是主動觸發的,提供了上下文菜單,我們可以驗證我們整個模型或者是單個元素。
還是以我們的例子來介紹,為了狀態機的合理性,我們需要遵守:
Name屬性的值的有效性
初始狀態不能做為轉移的目標,結束狀態不能做為轉移的開始.
一個狀態機應該有一個初始狀態
下面我們就一步一步添加這些Validation:
1.首先在我們在CustomCode文件夾下面添加Validation文件夾,來存儲我們的驗證.
2.新建一個partial類State,注意命名空間修改為CompanyName.LanguageSm,要保持和原來生成的State類一致.
3.給類打上ValidationState屬性標記.
-
[ValidationState(ValidationState.Enabled)]
-
public partial class State
-
{ -
}
4. 我們在partial類中添加自定義的驗證,其實也就是添加驗證方法,在方法上打上ValidationMehtods標記
-
[ValidationState(ValidationState.Enabled)]
-
public partial class State
-
{ -
[ValidationMethod(ValidationCategories.Open | ValidationCategories.Save | ValidationCategories.Menu)]
-
private void ValidateAttributeNameAsValidIdentifier(ValidationContext context)
-
{ -
CSharpCodeProvider csharp = new CSharpCodeProvider();
-
if (string.IsNullOrEmpty(this.Name.Trim()))
-
context.LogError("State的名稱不能為空", "StateMachine – State - 01", this);
-
else if (!csharp.IsValidIdentifier(Name)) -
{ -
context.LogError("State的名稱不合法", "StateMachines – State - 02", this);
-
}
-
}
-
}
其中ValidationCategories是代表驗證的調用時機, 設計器打開.保存模型文件還是通過右鍵菜單中的Validate. 參數ValidationContext包含驗證的上下文信息,上面我們用來記錄錯誤,信息提示等.
同樣的方式我們添加對另外初始狀態的驗證,上面我們提示的信息是寫死在程序里面的,當然可以實現從資源里獲取.
5.我們要驗證整個狀態機只有一個初始狀態怎么辦,添加StateMachine的partial類,在這個類里面添加驗證:
-
[ValidationState(ValidationState.Enabled)]
-
public partial class StateMachine
-
{ -
[ValidationMethod(ValidationCategories.Open | ValidationCategories.Save | ValidationCategories.Menu)]
-
private void ValidateStateMachineHasOneInitialState(ValidationContext context)
-
{ -
if(!States.Exists(p=>p.Kind == StateKind.Initial))
-
context.LogError("狀態機至少應該有一個初始狀態", "StateMachine - 01", this);
-
}
-
}
6.不過這還不夠,我們還需要進行一下設置才能讓驗證生效,打開dsl文件,Dsl Explor中的Editor/Validation結點,設置Validation屬性中的Use Menu,Use Open,Use Save為True:
7.重新轉換所有的模板,我們來測試一下我們的驗證,右鍵全部驗證:
![]()
回過頭來想一下,我們不允許結束狀態上建立Transition的源,這應該是強制性的吧,既然這樣,我們為什么還要在驗證的時候才去驗證,而允許用戶有機會犯這樣的錯誤呢?下面我們就來實現對這個的控制,使結束狀態為源不能夠建立Transition關系.
8.打開Dsl Explorer,結點Connection Builder/TransitionBuilder/Link Connect Directives/Transition,選中Transition后,打開Dsl Details窗口,我們設置域關系Transition的對于源角色State使用自定義接受(Custom accept).![]()
9.轉換所有模板,重新編譯解決方案,你會發現有錯誤發生,是提示你必須要實現TransitionBuilder中的CanAcceptStateAsSource和CanAcceptStateAndStateAsSourceAndTarget方法,這是vs.net dsl設計自定義處理后通用的處理方法,你必須手動添加相應的方法后才能夠編譯通過. 在我們的CustomCode文件夾下面添加TransitionBuilder類,注意這是一個靜態類,然后在這個靜態類中添加這兩個靜態方法.
-
namespace Company.LanguageSm -
{ -
public static partial class TransitionBuilder
-
{ -
private static bool CanAcceptStateAsSource(State state)
-
{ -
return ((state != null) && (state.Kind != StateKind.Final));
-
}
-
private static bool CanAcceptStateAndStateAsSourceAndTarget(State sourceState, State targetState)
-
{ -
return CanAcceptSource(sourceState); -
}
-
}
-
}
10.再次運行我們的項目,你會發現,當選中Transition在一個結束狀態上拖拽時,你會發現,顯示圓形的不可用.
代碼下載
參考資源
1. Visual Stuido DSL 工具特定領域開發指南
2. DSL Tools Lab http://code.msdn.microsoft.com/DSLToolsLab 系列教程 [本系列的入門案例的主要參考]
作者:孤獨俠客(似水流年)
出處:http://lonely7345.cnblogs.com/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

浙公網安備 33010602011771號