调用活动验证

活动验证提供一种方法,用于在执行之前识别和报告任何活动的配置中的错误。 当在工作流设计器中修改工作流时验证进行,并且任何验证错误或警告显示在工作流设计器中。 当工作流被调用并且发生任何验证错误时,验证也会在运行时进行,默认验证逻辑会引发 InvalidWorkflowException 。 Windows Workflow Foundation (WF)提供了一个名为ActivityValidationServices的类,供工作流应用程序和工具开发人员用于显式验证活动。 本主题介绍如何用于 ActivityValidationServices 执行活动验证。

使用 ActivityValidationServices

ActivityValidationServices 有两个 Validate 重载,用于调用活动的验证逻辑。 第一个重载会获取要验证的根活动,并返回包含验证错误和警告的集合。 在以下示例中,使用具有两个必需参数的自定义 Add 活动。

public sealed class Add : CodeActivity<int>
{
    [RequiredArgument]
    public InArgument<int> Operand1 { get; set; }

    [RequiredArgument]
    public InArgument<int> Operand2 { get; set; }

    protected override int Execute(CodeActivityContext context)
    {
        return Operand1.Get(context) + Operand2.Get(context);
    }
}

活动 Add 在一个 Sequence内部使用,但其两个必需参数未绑定,如以下示例所示。

Variable<int> Operand1 = new Variable<int>{ Default = 10 };
Variable<int> Operand2 = new Variable<int>{ Default = 15 };
Variable<int> Result = new Variable<int>();

Activity wf = new Sequence
{
    Variables = { Operand1, Operand2, Result },
    Activities =
    {
        new Add(),
        new WriteLine
        {
            Text = new InArgument<string>(env => "The result is " + Result.Get(env))
        }
    }
};

可以通过调用 Validate此工作流进行验证。 Validate 返回活动包含的任何验证错误或警告的集合,以及任何子项,如以下示例所示。

ValidationResults results = ActivityValidationServices.Validate(wf);

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: {0}", error.Message);
    }
    foreach (ValidationError warning in results.Warnings)
    {
        Console.WriteLine("Warning: {0}", warning.Message);
    }
}

在此示例工作流上调用时 Validate ,将返回两个验证错误。

错误:未提供所需的活动参数“Operand2”的值。错误:未提供所需的活动参数“Operand1”的值。 如果调用了此工作流,则会引发一个 InvalidWorkflowException 工作流,如以下示例所示。

try
{
    WorkflowInvoker.Invoke(wf);
}
catch (Exception ex)
{
    Console.WriteLine(ex);
}

System.Activities.InvalidWorkflowException:处理工作流树时遇到以下错误:“Add”:未提供所需的活动参数“Operand2”的值。“Add”:未提供所需的活动参数“Operand1”的值。 要使此示例工作流有效,必须绑定活动的两个必需参数 Add 。 在以下示例中,这两个必需参数与结果值一起绑定到工作流变量。 在此示例中,该 Result 参数与两个必需参数绑定。 Result参数不需要绑定,如果未绑定,则不会导致验证错误。 如果工作流中需要在其他位置使用 Result 的值,那么由工作流作者负责绑定它。

new Add
{
    Operand1 = Operand1,
    Operand2 = Operand2,
    Result = Result
}

验证根活动中的必需参数

如果工作流的根活动具有参数,则在调用工作流并将参数传递给工作流之前,这些活动不会绑定。 以下工作流通过验证,但如果在不传入所需参数的情况下调用工作流,则会引发异常,如以下示例所示。

Activity wf = new Add();

ValidationResults results = ActivityValidationServices.Validate(wf);
// results has no errors or warnings, but when the workflow
// is invoked, an InvalidWorkflowException is thrown.
try
{
    WorkflowInvoker.Invoke(wf);
}
catch (Exception ex)
{
    Console.WriteLine(ex);
}

System.ArgumentException:根活动的参数设置不正确。修复工作流定义或提供输入值以修复以下错误:“Add”:未提供所需活动参数“Operand2”的值。“Add”:未提供所需的活动参数“Operand1”的值。 传递正确的参数后,工作流将成功完成,如以下示例所示。

Add wf = new Add();

ValidationResults results = ActivityValidationServices.Validate(wf);
// results has no errors or warnings, and the workflow completes
// successfully because the required arguments were passed.
try
{
    Dictionary<string, object> wfparams = new Dictionary<string, object>
    {
        { "Operand1", 10 },
        { "Operand2", 15 }
    };

    int result = WorkflowInvoker.Invoke(wf, wfparams);
    Console.WriteLine("Result: {0}", result);
}
catch (Exception ex)
{
    Console.WriteLine(ex);
}

注释

在此示例中,根活动声明为 Add ,而不是 Activity 如上一示例中所示。 通过这种方式,WorkflowInvoker.Invoke 方法可以返回一个表示 Add 活动结果的整数,而不是一个参数字典 outwf变量也可以声明为 Activity<int>

验证根参数时,主机应用程序有责任确保在调用工作流时传递所有必需的参数。

调用命令式基于代码的验证

代码驱动的命令式验证为活动提供了一种简单的方法来验证自身,并且适用于从CodeActivityAsyncCodeActivityNativeActivity派生的活动。 将用于检测任何验证错误或警告的验证代码添加到活动中。 在活动上调用验证时,这些警告或错误包含在调用 Validate 返回的集合中。 在以下示例中,定义了一个 CreateProduct 活动。 如果Cost大于Price,则会将验证错误添加到CacheMetadata重写中的元数据中。

public sealed class CreateProduct : CodeActivity
{
    public double Price { get; set; }
    public double Cost { get; set; }

    // [RequiredArgument] attribute will generate a validation error
    // if the Description argument is not set.
    [RequiredArgument]
    public InArgument<string> Description { get; set; }

    protected override void CacheMetadata(CodeActivityMetadata metadata)
    {
        base.CacheMetadata(metadata);
        // Determine when the activity has been configured in an invalid way.
        if (this.Cost > this.Price)
        {
            // Add a validation error with a custom message.
            metadata.AddValidationError("The Cost must be less than or equal to the Price.");
        }
    }

    protected override void Execute(CodeActivityContext context)
    {
        // Not needed for the sample.
    }
}

在此示例中,使用 CreateProduct 活动配置工作流。 在此工作流中,Cost 大于 Price,并且未设置所需的 Description 参数。 调用验证时,将返回以下错误。

Activity wf = new Sequence
{
    Activities =
    {
        new CreateProduct
        {
            Cost = 75.00,
            Price = 55.00
            // Cost > Price and required Description argument not set.
        },
        new WriteLine
        {
            Text = "Product added."
        }
    }
};

ValidationResults results = ActivityValidationServices.Validate(wf);

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: {0}", error.Message);
    }
    foreach (ValidationError warning in results.Warnings)
    {
        Console.WriteLine("Warning: {0}", warning.Message);
    }
}

错误:成本必须小于或等于 Price。错误:未提供所需的活动参数“Description”的值。

注释

自定义活动作者可以在活动的 CacheMetadata 替代中提供验证逻辑。 引发 CacheMetadata 的任何异常都不会被视为验证错误。 这些异常将从调用Validate中抛出,并且必须由调用方处理。

使用 ValidationSettings

默认情况下,在调用 ActivityValidationServices验证时,会评估活动树中的所有活动。 ValidationSettings 通过配置验证的三个属性,可以通过多种不同的方式自定义验证。 SingleLevel 指定验证程序是应该遍遍整个活动树,还是只将验证逻辑应用于所提供的活动。 此值的默认值为 false. AdditionalConstraints 指定从类型到约束列表的其他约束映射。 对于正在验证的活动树中的每个活动的基类型,将查找至AdditionalConstraints。 如果找到匹配的约束列表,则会针对该活动评估列表中的所有约束。 OnlyUseAdditionalConstraints 指定验证程序是计算所有约束还是只计算在 AdditionalConstraints中指定的约束。 默认值为 falseAdditionalConstraintsOnlyUseAdditionalConstraints 对于工作流宿主作者来说非常有用,可以为工作流添加额外的验证,比如针对 FxCop 等工具的策略约束。 有关约束的详细信息,请参阅 声明性约束

若要使用 ValidationSettings,请配置所需的属性,然后将其传递到调用 Validate中。 在此示例中,将验证一个包含自定义 Add 活动的 Sequence 工作流。 活动 Add 具有两个必需参数。

public sealed class Add : CodeActivity<int>
{
    [RequiredArgument]
    public InArgument<int> Operand1 { get; set; }

    [RequiredArgument]
    public InArgument<int> Operand2 { get; set; }

    protected override int Execute(CodeActivityContext context)
    {
        return Operand1.Get(context) + Operand2.Get(context);
    }
}

以下 Add 活动在 Sequence 中使用,但其两个必需参数未绑定。

Variable<int> Operand1 = new Variable<int> { Default = 10 };
Variable<int> Operand2 = new Variable<int> { Default = 15 };
Variable<int> Result = new Variable<int>();

Activity wf = new Sequence
{
    Variables = { Operand1, Operand2, Result },
    Activities =
    {
        new Add(),
        new WriteLine
        {
            Text = new InArgument<string>(env => "The result is " + Result.Get(env))
        }
    }
};

对于以下示例,将 SingleLevel 设置为 true 并执行验证,因此仅验证根 Sequence 活动。

ValidationSettings settings = new ValidationSettings
{
    SingleLevel = true
};

ValidationResults results = ActivityValidationServices.Validate(wf, settings);

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: {0}", error.Message);
    }
    foreach (ValidationError warning in results.Warnings)
    {
        Console.WriteLine("Warning: {0}", warning.Message);
    }
}

此代码显示以下输出:

无警告或错误 即使 Add 活动具有未绑定的必需参数,验证也成功,因为只评估根活动。 这种类型的验证仅适用于验证活动树中的特定元素,例如验证设计器中单个活动的属性更改。 请注意,如果调用此工作流,将评估工作流中配置的完整验证,并引发验证 InvalidWorkflowExceptionActivityValidationServicesValidationSettings 只配置由主机明确调用的验证,而非调用工作流时发生的验证。