워크플로 정의는 구성된 활동 개체의 트리입니다. 이 활동 트리는 XAML을 직접 편집하거나 워크플로 디자이너를 사용하여 XAML을 생성하는 등 여러 가지 방법으로 정의할 수 있습니다. 그러나 XAML 사용은 요구 사항이 아닙니다. 워크플로 정의를 프로그래밍 방식으로 만들 수도 있습니다. 이 항목에서는 코드를 사용하여 워크플로 정의, 활동 및 식을 만드는 방법에 대한 개요를 제공합니다. 코드를 사용하여 XAML 워크플로를 사용하는 예제는 XAML을 오가는 워크플로 및 활동 직렬화를 참조하세요.
워크플로 정의 만들기
작업 형식의 인스턴스를 인스턴스화하고 활동 개체의 속성을 구성하여 워크플로 정의를 만들 수 있습니다. 자식 활동이 포함되지 않은 활동의 경우 몇 줄의 코드를 사용하여 수행할 수 있습니다.
Activity wf = new WriteLine
{
Text = "Hello World."
};
WorkflowInvoker.Invoke(wf);
비고
이 항목의 예제는 샘플 워크플로를 실행하는 데 사용합니다 WorkflowInvoker . 워크플로 호출, 인수 전달 및 사용 가능한 다양한 호스팅 선택에 대한 자세한 내용은 WorkflowInvoker 및 WorkflowApplication 사용을 참조하세요.
이 예제에서는 단일 WriteLine 활동으로 구성된 워크플로가 만들어집니다. WriteLine 활동의 Text 인수가 설정되고 워크플로가 호출됩니다. 활동에 자식 활동이 포함된 경우 생성 방법은 비슷합니다. 다음 예제에서는 두 개의 Sequence 활동을 포함하는 WriteLine 활동을 사용합니다.
Activity wf = new Sequence
{
Activities =
{
new WriteLine
{
Text = "Hello"
},
new WriteLine
{
Text = "World."
}
}
};
WorkflowInvoker.Invoke(wf);
객체 초기화자 사용
이 항목의 예에서는 개체 초기화 구문을 사용합니다. 개체 초기화 구문은 워크플로에 있는 활동의 계층적 뷰를 제공하고 활동 간의 관계를 표시하기 때문에 코드에서 워크플로 정의를 만드는 유용한 방법일 수 있습니다. 프로그래밍 방식으로 워크플로를 만들 때 개체 초기화 구문을 사용하기 위한 요구 사항은 없습니다. 다음 예는 이전 예와 기능적으로 동일합니다.
WriteLine hello = new WriteLine();
hello.Text = "Hello";
WriteLine world = new WriteLine();
world.Text = "World";
Sequence wf = new Sequence();
wf.Activities.Add(hello);
wf.Activities.Add(world);
WorkflowInvoker.Invoke(wf);
개체 이니셜라이저에 대한 자세한 내용은 방법: 생성자를 호출하지 않고 개체 초기화(C# 프로그래밍 가이드) 및 방법: 개체 이니셜라이저를 사용하여 개체 선언을 참조하세요.
변수, 리터럴 값, 그리고 식에 대한 작업
코드를 사용하여 워크플로 정의를 만들 때는 워크플로 정의 만들기의 일부로 실행되는 코드와 해당 워크플로 인스턴스 실행의 일부로 실행되는 코드에 주의해야 합니다. 예를 들어 다음 워크플로는 난수를 생성하여 콘솔에 쓰기 위한 것입니다.
Variable<int> n = new Variable<int>
{
Name = "n"
};
Activity wf = new Sequence
{
Variables = { n },
Activities =
{
new Assign<int>
{
To = n,
Value = new Random().Next(1, 101)
},
new WriteLine
{
Text = new InArgument<string>((env) => "The number is " + n.Get(env))
}
}
};
이 워크플로 정의 코드가 실행되면 호출 Random.Next
이 수행되고 결과가 워크플로 정의에 리터럴 값으로 저장됩니다. 이 워크플로의 많은 인스턴스를 호출할 수 있으며 모두 동일한 숫자를 표시합니다. 워크플로 실행 중에 난수를 생성하려면 워크플로가 실행될 때마다 계산되는 식을 사용해야 합니다. 다음 예제에서는 Visual Basic 식이 VisualBasicValue<TResult>와 함께 사용됩니다.
new Assign<int>
{
To = n,
Value = new VisualBasicValue<int>("New Random().Next(1, 101)")
}
이전 예제의 식은 a CSharpValue<TResult> 및 C# 식을 사용하여 구현할 수도 있습니다.
new Assign<int>
{
To = n,
Value = new CSharpValue<int>("new Random().Next(1, 101)")
}
C# 식을 포함하는 워크플로가 호출되기 전에 컴파일해야 합니다. 컴파일되지 않은 C# 식이 있을 경우, 워크플로가 다음과 유사한 메시지를 발생시키며 예외가 NotSupportedException 발생합니다. Visual Studio에서 생성된 워크플로와 관련된 대부분의 시나리오에서는 C# 식이 자동으로 컴파일되지만, 코드 워크플로와 같은 특정 시나리오에서는 C# 식을 수동으로 컴파일해야 합니다. C# 식을 컴파일하는 방법에 대한 예제는 C# 식 항목의 코드 워크플로 섹션에서 C# 식 사용 섹션을 참조 하세요 .
A VisualBasicValue<TResult> 는 식에서 r-value로 사용할 수 있는 Visual Basic 구문의 식을 나타내고 CSharpValue<TResult> 식에서 r-value로 사용할 수 있는 C# 구문의 식을 나타냅니다. 이러한 식은 포함된 작업이 실행될 때마다 평가됩니다. 식의 결과가 워크플로 변수 n
에 할당되고 이러한 결과는 워크플로의 다음 작업에서 사용됩니다. 런타임에 워크플로 변수 n
의 값에 액세스하려면 이 값이 ActivityContext 필요합니다. 다음 람다 식을 사용하여 액세스할 수 있습니다.
비고
이러한 두 코드는 모두 C#을 프로그래밍 언어로 사용하는 예제이지만, 하나는 A VisualBasicValue<TResult> 를 사용하고 다른 하나는 C#을 CSharpValue<TResult>사용합니다. VisualBasicValue<TResult> Visual Basic 및 CSharpValue<TResult> C# 프로젝트에서 모두 사용할 수 있습니다. 기본적으로 워크플로 디자이너에서 만든 식은 호스팅 프로젝트의 언어와 일치합니다. 코드에서 워크플로를 만들 때 원하는 언어는 워크플로 작성자의 재량에 따라 결정됩니다.
이 예제에서는 식의 결과가 워크플로 변수 n
에 할당되며 이러한 결과는 워크플로의 다음 작업에서 사용됩니다. 런타임에 워크플로 변수 n
의 값에 액세스하려면 이 값이 ActivityContext 필요합니다. 다음 람다 식을 사용하여 액세스할 수 있습니다.
new WriteLine
{
Text = new InArgument<string>((env) => "The number is " + n.Get(env))
}
람다 식에 대한 자세한 내용은 람다 식(C# 참조) 또는 람다 식(Visual Basic)을 참조하세요.
람다 식은 XAML 형식으로 직렬화할 수 없습니다. 람다 식으로 워크플로를 직렬화하려고 시도하면, 다음 메시지가 포함된 예외가 LambdaSerializationException와 함께 throw됩니다: "이 워크플로에는 코드에 지정된 람다 식이 포함됩니다." 이러한 식은 XAML 직렬화할 수 없습니다. 워크플로 XAML 직렬화 가능하도록 하려면 VisualBasicValue/VisualBasicReference 또는 ExpressionServices.Convert(람다)를 사용합니다. 이렇게 함으로써 람다 식이 식 작업으로 변환됩니다. 이 식을 XAML과 호환되도록 하려면 다음 예제와 같이 ExpressionServices 및 Convert를 사용합니다.
new WriteLine
{
//Text = new InArgument<string>((env) => "The number is " + n.Get(env))
Text = ExpressionServices.Convert((env) => "The number is " + n.Get(env))
}
A VisualBasicValue<TResult> 를 사용할 수도 있습니다. Visual Basic 식을 사용할 때는 람다 식이 필요하지 않습니다.
new WriteLine
{
//Text = new InArgument<string>((env) => "The number is " + n.Get(env))
//Text = ExpressionServices.Convert((env) => "The number is " + n.Get(env))
Text = new VisualBasicValue<string>("\"The number is \" + n.ToString()")
}
런타임에 Visual Basic 식은 LINQ 식으로 컴파일됩니다. 앞의 두 예제는 모두 XAML로 직렬화할 수 있습니다. 그러나 직렬화된 XAML을 워크플로 디자이너에서 보고 편집하려는 경우에는 식에 VisualBasicValue<TResult>를 사용하세요. 사용하는 ExpressionServices.Convert
직렬화된 워크플로는 디자이너에서 열 수 있지만 식 값은 비어 있습니다. 워크플로를 XAML로 직렬화하는 방법에 대한 자세한 내용은 XAML에서 워크플로 및 활동 직렬화를 참조하세요.
리터럴 식 및 참조 형식
리터럴 식은 워크플로에서 Literal<T> 활동으로 표시됩니다. 다음 WriteLine 활동은 기능적으로 동일합니다.
new WriteLine
{
Text = "Hello World."
},
new WriteLine
{
Text = new Literal<string>("Hello World.")
}
을 제외한 String모든 참조 형식으로 리터럴 식을 초기화하는 것은 유효하지 않습니다. 다음 예에서는 Assign 활동의 Value 속성을 List<string>
를 사용하여 리터럴 표현식으로 초기화합니다.
new Assign
{
To = new OutArgument<List<string>>(items),
Value = new InArgument<List<string>>(new List<string>())
},
이 작업이 포함된 워크플로의 유효성을 검사하면 다음과 같은 유효성 검사 오류가 반환됩니다. "리터럴은 값 형식과 변경할 수 없는 형식 System.String만 지원합니다. System.Collections.Generic.List'1[System.String] 형식은 리터럴로 사용할 수 없습니다." 워크플로가 호출되면 InvalidWorkflowException 유효성 검사 오류의 텍스트가 포함된 워크플로가 throw됩니다. 참조 형식을 사용하여 리터럴 식을 만들면 워크플로의 각 인스턴스에 대한 참조 형식의 새 인스턴스가 만들어지지 않으므로 유효성 검사 오류입니다. 이 문제를 해결하려면 리터럴 식을 참조 형식의 새 인스턴스를 생성하고 반환하는 식으로 바꿉니다.
new Assign
{
To = new OutArgument<List<string>>(items),
Value = new InArgument<List<string>>(new VisualBasicValue<List<string>>("New List(Of String)"))
},
식에 대한 자세한 내용은 식을 참조하세요.
식 및 InvokeMethod 작업을 사용하여 개체에 메서드 호출
이 InvokeMethod<TResult> 작업을 사용하여 .NET Framework에서 클래스의 정적 메서드 및 인스턴스 메서드를 호출할 수 있습니다. 이 항목의 이전 예제에서는 클래스를 사용하여 Random 난수를 생성했습니다.
new Assign<int>
{
To = n,
Value = new VisualBasicValue<int>("New Random().Next(1, 101)")
}
이 InvokeMethod<TResult> 활동은 Next 클래스의 Random 메서드를 호출하는 데 사용되었을 수도 있습니다.
new InvokeMethod<int>
{
TargetObject = new InArgument<Random>(new VisualBasicValue<Random>("New Random()")),
MethodName = "Next",
Parameters =
{
new InArgument<int>(1),
new InArgument<int>(101)
},
Result = n
}
Next 정적 메서드가 아니므로 클래스의 Random 인스턴스가 속성에 TargetObject 대해 제공됩니다. 이 예제에서는 Visual Basic 식을 사용하여 새 인스턴스를 만들지만 이전에 만들어 워크플로 변수에 저장되었을 수도 있습니다. 이 예제에서는 Assign<T> 활동을 사용하는 것이 InvokeMethod<TResult> 활동을 사용하는 것보다 더 간단합니다. 메서드 호출이 Assign<T> 또는 InvokeMethod<TResult> 활동에 의해 최종적으로 실행될 때, 이것이 오래 실행되는 경우 InvokeMethod<TResult>는 RunAsynchronously 속성이 있어 장점이 있습니다. 이 속성을 설정 true
하면 호출된 메서드가 워크플로와 관련하여 비동기적으로 실행됩니다. 다른 활동이 병렬인 경우 메서드가 비동기적으로 실행되는 동안 차단되지 않습니다. 또한 호출할 메서드에 반환 값 InvokeMethod<TResult> 이 없는 경우 메서드를 호출하는 적절한 방법입니다.
인수 및 동적 활동
작업 트리에 활동을 어셈블하고 속성과 인수를 구성하여 코드에서 워크플로 정의를 만듭니다. 기존 인수는 바인딩할 수 있지만 새 인수는 활동에 추가할 수 없습니다. 여기에는 루트 활동에 전달된 워크플로 인수가 포함됩니다. 명령적 코드에서 워크플로 인수는 새 CLR 형식의 속성으로 지정되고, XAML에서는 x:Class
및 x:Member
을 사용하여 선언됩니다. 워크플로 정의를 메모리 내 개체의 트리로 만들 때 만들어지는 새 CLR 형식이 없으므로 인수를 추가할 수 없습니다. 그러나 인수는 DynamicActivity에 추가할 수 있습니다. 이 예제에서는 두 개의 정수 인수를 받아서 더하고 결과를 반환하는 DynamicActivity<TResult>가 생성됩니다. 각 인수에 대해 DynamicActivityProperty가 만들어지고, 작업의 결과가 Result의 DynamicActivity<TResult> 인수에 할당됩니다.
InArgument<int> Operand1 = new InArgument<int>();
InArgument<int> Operand2 = new InArgument<int>();
DynamicActivity<int> wf = new DynamicActivity<int>
{
Properties =
{
new DynamicActivityProperty
{
Name = "Operand1",
Type = typeof(InArgument<int>),
Value = Operand1
},
new DynamicActivityProperty
{
Name = "Operand2",
Type = typeof(InArgument<int>),
Value = Operand2
}
},
Implementation = () => new Sequence
{
Activities =
{
new Assign<int>
{
To = new ArgumentReference<int> { ArgumentName = "Result" },
Value = new InArgument<int>((env) => Operand1.Get(env) + Operand2.Get(env))
}
}
}
};
Dictionary<string, object> wfParams = new Dictionary<string, object>
{
{ "Operand1", 25 },
{ "Operand2", 15 }
};
int result = WorkflowInvoker.Invoke(wf, wfParams);
Console.WriteLine(result);
동적 활동에 대한 자세한 내용은 런타임에 활동 만들기를 참조하세요.
모인 활동
동적 활동은 코드를 사용하는 인수를 포함하는 활동을 정의하는 한 가지 방법이지만 코드에서 작업을 만들고 형식으로 컴파일할 수도 있습니다. CodeActivity에서 파생된 단순 활동과 AsyncCodeActivity에서 파생된 비동기 활동을 만들 수 있습니다. 이러한 활동에는 인수가 있고, 값을 반환하며, 명령적 코드를 사용하여 논리를 정의할 수 있습니다. 이러한 유형의 활동을 만드는 예제는 CodeActivity 기본 클래스 및 비동기 활동 만들기를 참조하세요.
NativeActivity으로부터 파생된 활동은 명령적 코드를 사용하여 논리를 정의할 수 있으며, 논리를 정의하는 자식 활동도 포함할 수 있습니다. 또한 책갈피 만들기와 같은 런타임 기능에 대한 모든 권한이 있습니다. NativeActivity 기반 작업을 만드는 예제는 NativeActivity 기본 클래스, 활동 만들기 방법, 및 네이티브 활동을 사용한 사용자 지정 복합 샘플을 참조하세요.
Activity에서 파생된 활동은 오직 자식 활동을 사용하여 논리를 정의합니다. 이러한 활동은 일반적으로 워크플로 디자이너를 사용하여 생성되지만 코드를 사용하여 정의할 수도 있습니다. 다음 예제에서는 Square
활동이 Activity<int>
에서 파생되는 것을 정의합니다.
Square
활동에는 InArgument<T> 이름이 Value
인 단일 활동이 있으며, Sequence 속성을 사용하여 Implementation 활동을 지정함으로써 해당 논리를 정의합니다. 활동에는 Sequence 활동, WriteLine 활동 및 Assign<T> 활동이 포함됩니다. 이러한 세 가지 활동은 함께 활동의 논리를 구현합니다 Square
.
class Square : Activity<int>
{
[RequiredArgument]
public InArgument<int> Value { get; set; }
public Square()
{
this.Implementation = () => new Sequence
{
Activities =
{
new WriteLine
{
Text = new InArgument<string>((env) => "Squaring the value: " + this.Value.Get(env))
},
new Assign<int>
{
To = new OutArgument<int>((env) => this.Result.Get(env)),
Value = new InArgument<int>((env) => this.Value.Get(env) * this.Value.Get(env))
}
}
};
}
}
다음 예제에서는 단일 Square
활동으로 구성된 워크플로 정의가 .를 사용하여 WorkflowInvoker호출됩니다.
Dictionary<string, object> inputs = new Dictionary<string, object> {{ "Value", 5}};
int result = WorkflowInvoker.Invoke(new Square(), inputs);
Console.WriteLine("Result: {0}", result);
워크플로가 호출되면 콘솔에 다음 출력이 표시됩니다.
값을 제곱하기: 5
결과: 25
.NET