• 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:

    隐藏行号 复制代码
    1. internal sealed partial class ConditionPropertyHandler : DomainPropertyValueHandler<Transition, string>
    2.    {
    3.        protected override void OnValueChanging(Transition element, string oldValue, string newValue)
    4.        {
    5.            if (!element.Store.InUndoRedoOrRollback)
    6.            {
    7.                if (!string.IsNullOrEmpty(newValue))
    8.                {
    9.                    element.Label = ComputeSummary(newValue, element.Condition, element.Action);
    10.                }
    11.            }
    12.            base.OnValueChanging(element, oldValue, newValue);
    13.        }
    14.    }

        介绍了硬约束后,我们来看一下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属性标记.

    隐藏行号 复制代码
    1. [ValidationState(ValidationState.Enabled)]
    2.   public partial class State
    3. {
    4.   }

          4. 我们在partial类中添加自定义的验证,其实也就是添加验证方法,在方法上打上ValidationMehtods标记

    隐藏行号 复制代码
    1. [ValidationState(ValidationState.Enabled)]
      
    2.  public partial class State
      
    3. {
      
    4.      [ValidationMethod(ValidationCategories.Open | ValidationCategories.Save | ValidationCategories.Menu)]
      
    5.      private void ValidateAttributeNameAsValidIdentifier(ValidationContext context)
      
    6.      {
      
    7.          CSharpCodeProvider csharp = new CSharpCodeProvider();
      
    8.          if (string.IsNullOrEmpty(this.Name.Trim()))
      
    9.              context.LogError("State的名称不能为空", "StateMachine – State - 01", this);   
      
    10. 
      
    11.          else if (!csharp.IsValidIdentifier(Name))
      
    12.          {
      
    13.              context.LogError("State的名称不合法", "StateMachines – State - 02", this);
      
    14.          }
      
    15.      }
      
    16.  }
      

       其中ValidationCategories是代表验证的调用时机, 设计器打开.保存模型文件还是通过右键菜单中的Validate. 参数ValidationContext包含验证的上下文信息,上面我们用来记录错误,信息提示等.
       同样的方式我们添加对另外初始状态的验证,上面我们提示的信息是写死在程序里面的,当然可以实现从资源里获取.
         5.我们要验证整个状态机只有一个初始状态怎么办,添加StateMachine的partial类,在这个类里面添加验证:

    隐藏行号 复制代码
    1. [ValidationState(ValidationState.Enabled)]
      
    2. public partial class StateMachine
      
    3. {
      
    4.     [ValidationMethod(ValidationCategories.Open | ValidationCategories.Save | ValidationCategories.Menu)]
      
    5.     private void ValidateStateMachineHasOneInitialState(ValidationContext context)
      
    6.     {
      
    7.        if(!States.Exists(p=>p.Kind == StateKind.Initial))
      
    8.             context.LogError("状态机至少应该有一个初始状态", "StateMachine - 01", this);
      
    9.     }
      
    10. }
      


         6.不过这还不够,我们还需要进行一下设置才能让验证生效,打开dsl文件,Dsl Explor中的Editor/Validation结点,设置Validation属性中的Use Menu,Use Open,Use Save为True:
       image 
       

        7.重新转换所有的模板,我们来测试一下我们的验证,右键全部验证:
       C8Q@_LN7M5R}3{])GYTL]3F

       回过头来想一下,我们不允许结束状态上建立Transition的源,这应该是强制性的吧,既然这样,我们为什么还要在验证的时候才去验证,而允许用户有机会犯这样的错误呢?下面我们就来实现对这个的控制,使结束状态为源不能够建立Transition关系.
       8.打开Dsl Explorer,结点Connection Builder/TransitionBuilder/Link Connect Directives/Transition,选中Transition后,打开Dsl Details窗口,我们设置域关系Transition的对于源角色State使用自定义接受(Custom accept).
    @8U9QNZJ8W`00$KOCE@G}9W

        9.转换所有模板,重新编译解决方案,你会发现有错误发生,是提示你必须要实现TransitionBuilder中的CanAcceptStateAsSource和CanAcceptStateAndStateAsSourceAndTarget方法,这是vs.net dsl设计自定义处理后通用的处理方法,你必须手动添加相应的方法后才能够编译通过. 在我们的CustomCode文件夹下面添加TransitionBuilder类,注意这是一个静态类,然后在这个静态类中添加这两个静态方法.

    隐藏行号 复制代码
    1. namespace Company.LanguageSm
      
    2. {
      
    3.     public static partial class TransitionBuilder
      
    4. {
      
    5.         private static bool CanAcceptStateAsSource(State state)
      
    6.         {
      
    7.             return ((state != null) && (state.Kind != StateKind.Final));
      
    8.         }
      
    9.         private static bool CanAcceptStateAndStateAsSourceAndTarget(State sourceState, State targetState)
      
    10.         {
      
    11.             return CanAcceptSource(sourceState);
      
    12.         }
      
    13.     }
      
    14. }
      

        10.再次运行我们的项目,你会发现,当选中Transition在一个结束状态上拖拽时,你会发现,显示圆形的不可用.

    代码下载

    参考资源
          1. Visual Stuido DSL 工具特定领域开发指南
          2. DSL Tools Lab     http://code.msdn.microsoft.com/DSLToolsLab  系列教程  [本系列的入门案例的主要参考]

    作者:孤独侠客似水流年
    出处:http://lonely7345.cnblogs.com/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    [树形dp] Luogu P4516 潜入行动
    [kruskal][Trie] Codeforces 888G Xor-MST
    [线性基] Luogu P4151 最大XOR和路径
    [线段树] Luogu P4560 砖墙
    [递归][重心] Luogu P4886 快递员
    [Trie][贪心][堆] LibreOJ #3048 异或粽子
    [长链剖分][优先队列] LibreOJ #3052 春节十二响
    [支配树] Bzoj P2815 灾难
    [长链剖分][线段树] Bzoj P1758 重建计划
    [dsu on tree] Codeforces 600E Lomsat gelral
  • 原文地址:https://www.cnblogs.com/lonely7345/p/1679986.html
Copyright © 2020-2023  润新知