声明性约束

声明性约束为活动及其与其他活动的关系提供了强大的验证方法。 约束是在创作过程中为活动配置的,但工作流主机也可以指定其他约束。 本主题概述如何使用声明性约束来提供活动验证。

使用声明性约束

约束是包含验证逻辑的活动。 此约束活动可以在代码或 XAML 中创作。 创建约束活动后,活动作者会将此约束添加到Constraints的活动属性中进行验证,或者通过在AdditionalConstraints实例中使用ValidationSettings属性来提供额外的验证。 验证逻辑可以包含简单的验证,例如验证活动的元数据,但它还可以执行验证,以考虑当前活动与其父、子级和同级活动之间的关系。 通过使用Constraint<T>活动来编写约束,并提供了一些附加的验证活动,以帮助创建验证错误和警告,并提供有关工作流中相关活动的信息。

AssertValidation 和 AddValidationError

AssertValidation 活动计算其 Assertion 属性引用的表达式,如果表达式的计算结果为 false,则会向 ValidationResults 添加验证错误或警告。 该 Message 属性描述验证错误,属性 IsWarning 指示验证失败是错误还是警告。 IsWarning 的默认值为 false

在下面的示例中,声明了一个约束,如果 DisplayName 正在验证的活动长度为两个字符或更少,则会返回验证警告。 用于 Constraint<T> 的泛型类型参数指定该约束验证的活动类型。 此约束用作 Activity 泛型类型,可用于验证所有类型的活动。

public static Constraint ActivityDisplayNameIsNotSetWarning()  
{  
    DelegateInArgument<Activity> element = new DelegateInArgument<Activity>();  
  
    return new Constraint<Activity>  
    {  
        Body = new ActivityAction<Activity, ValidationContext>  
        {  
            Argument1 = element,  
            Handler = new AssertValidation  
            {  
                IsWarning = true,  
                Assertion = new InArgument<bool>(env => (element.Get(env).DisplayName.Length > 2)),  
                Message = new InArgument<string>("It is a best practice to have a DisplayName of more than 2 characters."),  
            }  
        }  
    };  
}  

若要为活动指定此约束,则会将其添加到 Constraints 活动中,如以下示例代码所示。

public sealed class SampleActivity : CodeActivity  
{  
    public SampleActivity()  
    {  
        base.Constraints.Add(ActivityDisplayNameIsNotSetWarning());  
    }  
  
    // Activity implementation omitted.  
}  

主机还可以使用 AdditionalConstraints 为工作流中的活动指定此约束,下一部分对此进行了介绍。

AddValidationError 活动用于生成验证错误或警告,而无需计算表达式。 其属性类似于 AssertValidation ,它可与约束(如活动) If 的流控制活动结合使用。

工作流关系活动

提供了多个验证活动,提供有关工作流中与所验证活动相关的其他活动的信息。 GetParentChain 返回包含当前活动与根活动之间的所有活动的活动集合。 GetChildSubtree 提供一个活动集合,其中包含递归模式中的子活动,并 GetWorkflowTree 获取工作流中的所有活动。

下面的示例定义了一个 CreateState 活动。 活动 CreateState 必须包含在 CreateCountry 活动中,而且 GetParent 方法返回一个用来强制执行此要求的约束。 GetParent 使用 GetParentChain 活动与 ForEach<T> 活动结合,检查 CreateState 活动的父活动,以确定是否满足要求。

public sealed class CreateState : CodeActivity  
{  
    public CreateState()  
    {  
        base.Constraints.Add(CheckParent());  
        this.Cities = new List<Activity>();
    }  
  
    public List<Activity> Cities { get; set; }  
  
    public string Name { get; set; }
  
    static Constraint CheckParent()  
    {  
        DelegateInArgument<CreateState> element = new DelegateInArgument<CreateState>();  
        DelegateInArgument<ValidationContext> context = new DelegateInArgument<ValidationContext>();
        Variable<bool> result = new Variable<bool>();  
        DelegateInArgument<Activity> parent = new DelegateInArgument<Activity>();  
  
        return new Constraint<CreateState>  
        {
            Body = new ActivityAction<CreateState,ValidationContext>  
            {
                Argument1 = element,  
                Argument2 = context,  
                Handler = new Sequence  
                {  
                    Variables =  
                    {  
                        result
                    },  
                    Activities =  
                    {  
                        new ForEach<Activity>  
                        {
                            Values = new GetParentChain  
                            {  
                                ValidationContext = context
                            },  
                            Body = new ActivityAction<Activity>  
                            {
                                Argument = parent,
                                Handler = new If()  
                                {
                                    Condition = new InArgument<bool>((env) => object.Equals(parent.Get(env).GetType(),typeof(CreateCountry))),
                                    Then = new Assign<bool>  
                                    {  
                                        Value = true,  
                                        To = result  
                                    }  
                                }  
                            }
                        },  
                        new AssertValidation  
                        {  
                            Assertion = new InArgument<bool>(result),  
                            Message = new InArgument<string> ("CreateState has to be inside a CreateCountry activity"),
                        }  
                    }  
                }  
            }  
        };  
    }  
  
    protected override void Execute(CodeActivityContext context)  
    {  
        // not needed for the sample  
    }  
}  

其他约束

工作流宿主作者可以通过创建约束并将其添加到 AdditionalConstraints 实例字典 ValidationSettings 来为工作流中的活动指定其他验证约束。 每个项 AdditionalConstraints 都包含约束适用的活动类型,以及该类型的活动的其他约束的列表。 为工作流调用验证时,指定类型的每个活动(包括派生类)都会评估约束。 在此示例中, ActivityDisplayNameIsNotSetWarning 上一部分的约束应用于工作流中的所有活动。

Activity wf = new Sequence  
{  
    // Workflow Details Omitted.  
};  
  
ValidationSettings settings = new ValidationSettings()  
{  
  
    AdditionalConstraints =  
    {  
        {typeof(Activity), new List<Constraint> {ActivityDisplayNameIsNotSetWarning()}},
    }  
};  
  
// Validate the workflow.  
ValidationResults results = ActivityValidationServices.Validate(wf, settings);  
  
// Evaluate the results.  
if (results.Errors.Count == 0 && results.Warnings.Count == 0)  
{  
    Console.WriteLine("No warnings or errors");  
}  
else  
{  
    foreach (ValidationError error in results.Errors)  
    {  
        Console.WriteLine("Error in " + error.Source.DisplayName + ": " + error.Message);  
    }  
    foreach (ValidationError warning in results.Warnings)  
    {  
        Console.WriteLine("Warning in " + warning.Source.DisplayName + ": " + warning.Message);  
    }  
}  

如果OnlyUseAdditionalConstraintsValidationSettings属性是true,则在调用Validate进行验证时,只评估指定的附加约束。 这可用于检查特定验证配置的工作流。 请注意,当工作流被调用时,必须首先检查其配置的验证逻辑,只有通过验证后,工作流才能成功开始。 有关调用验证的详细信息,请参阅 调用活动验证