アクティビティ検証の呼び出し

このトピックの内容は、Windows Workflow Foundation 4 に該当します。

アクティビティの検証は、アクティビティを実行する前にアクティビティの構成エラーを特定および報告する手段です。検証が発生するのは、ワークフローがワークフロー デザイナーで修正され、検証エラーまたは警告がワークフロー デザイナーに表示されたときです。また、ワークフローが呼び出される実行時にも発生しますが、検証エラーが発生した場合は既定の検証ロジックによって InvalidWorkflowException がスローされます。Windows Workflow Foundation (WF) には、アクティビティを明示的に検証するためにワークフロー アプリケーションやツールの開発者が使用できる ActivityValidationServices クラスが用意されています。このトピックでは、ActivityValidationServices を使用してアクティビティの検証を実行する方法を説明します。

ActivityValidationServices の使用

ActivityValidationServices には、アクティビティの検証ロジックの呼び出しに使用される 2 つの Validate オーバーロードがあります。1 つ目のオーバーロードは、検証されるルート アクティビティを受け取り、一連の検証エラーと警告を返します。次の例では、2 つの必須引数を持つカスタムの 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 内で使用されていますが、次の例に示すように、その 2 つの必須引数はバインドされていません。

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);
    }
}
Ee358725.note(ja-jp,VS.100).gif注 :
カスタム アクティビティの作成者は、アクティビティの CacheMetadata オーバーライドに検証ロジックを指定できます。CacheMetadata からスローされる例外は、検証エラーとして処理されません。これらの例外は、Validate への呼び出しからエスケープされ、呼び出し元によって処理される必要があります。

このサンプル ワークフローで Validate が呼び出されると、次の 2 つの検証エラーが返されます。

エラー: 必須のアクティビティ引数 'Operand2' の値が指定されませんでした。
エラー: 必須のアクティビティ引数 'Operand1' の値が指定されませんでした。

このワークフローが呼び出された場合、次の例に示すように、InvalidWorkflowException がスローされることになります。

try
{
    WorkflowInvoker.Invoke(wf);
}
catch (Exception ex)
{
    Console.WriteLine(ex);
}
System.Activities.InvalidWorkflowException: 
ワークフロー ツリーの処理中に次のエラーが発生しました。
'Add': 必須のアクティビティ引数 'Operand2' の値が指定されませんでした。
'Add': 必須のアクティビティ引数 'Operand1' の値が指定されませんでした。

このサンプル ワークフローを有効にするには、Add アクティビティの 2 つの必須変数をバインドする必要があります。次の例では、2 つの必須変数と結果値がワークフロー変数にバインドされています。この例では、2 つの必須変数に加え、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: The root activity's argument settings are incorrect.
Either fix the workflow definition or supply input values to fix these errors:
'Add': Value for a required activity argument 'Operand2' was not supplied.
'Add': Value for a required activity argument 'Operand1' was not supplied.

正しい引数が渡されると、次の例に示すように、ワークフローは正常に完了します。

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);
}
Ee358725.note(ja-jp,VS.100).gif注 :
この例では、前の例の Activity とは異なり、ルート アクティビティは Add として宣言されています。このため、WorkflowInvoker.Invoke メソッドは、out 引数のディクショナリではなく、Add アクティビティの結果を表す 1 つの整数を返すことができます。また、変数 wfActivity<int> として宣言することも可能です。

ルート引数を検証する場合、ワークフローが呼び出されたときにすべての必須変数が渡されたことを確認するのはホスト アプリケーションが担当します。

ValidationSettings の使用

既定では、検証が ActivityValidationServices によって呼び出されたときにアクティビティ ツリーのすべてのアクティビティが評価されます。ValidationSettings を使用すると、その 3 つのプロパティを構成することによって、さまざまな方法で検証をカスタマイズできます。SingleLevel は、バリデーターがアクティビティ ツリー全体を調べるか、指定したアクティビティにのみ検証ロジックを適用するかを指定します。この値の既定値は false です。AdditionalConstraints は、型から制約のリストへの追加の制約マッピングを指定します。検証されているアクティビティ ツリーの各アクティビティの基本型について、AdditionalConstraints への参照が行われます。一致する制約リストが見つかると、アクティビティについて、リストのすべての制約が評価されます。OnlyUseAdditionalConstraints は、バリデーターがすべての制約を評価するか、AdditionalConstraints で指定された制約のみを評価するかを指定します。既定値は false です。AdditionalConstraints および OnlyUseAdditionalConstraints は、FxCop のようなツールのポリシーの制約など、ワークフローの検証をワークフロー ホストの作成者がさらに追加する場合に便利です。制約詳細情報、「宣言の制約」を参照してください。

ValidationSettings を使用するには、必要なプロパティを構成し、Validate の呼び出しで渡します。この例では、カスタムの Add アクティビティを持つ Sequence で構成されるワークフローが検証されます。この Add アクティビティには必須引数が 2 つあります。

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 で使用されますが、その 2 つの必須変数はバインドされていません。

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))
        }
    }
};

次の例では、SingleLeveltrue に設定されて検証が実行されるため、ルート アクティビティ 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);
    }
}

このコードを実行すると、次の出力が表示されます。

No warnings or errors

Add アクティビティにバインドされていない必須引数がある場合でも、ルート アクティビティのみが評価されたため、検証は正常に実行されています。このタイプの検証は、デザイナーで 1 つのアクティビティのプロパティの変更を検証するなど、アクティビティ ツリーの特定の要素のみを検証する場合に便利です。このワークフローが呼び出された場合、ワークフローで構成された完全な検証が評価され、InvalidWorkflowException がスローされます。ActivityValidationServices および ValidationSettings で構成されるのは、ホストによって明示的に呼び出される検証だけであり、ワークフローが呼び出されたときに発生する検証ではありません。