Jeremy Boyd
Intergen
January 2007
적용 대상 :
Windows Workflow Foundation
Windows Communication Foundation
Microsoft Visual Studio 2005
개요: Windows Workflow Foundation(WF)로 구축한 워크플로를 WCF(Windows
Communication Foundation) 서비스의 내부에서 호스트 하는 방법에 대해 설명합니다. 또, WCF에서 제공되는 다양한
기능을 사용하여 이중화 채널을 사용해 클라이언트 이벤트 콜백을 쉽게 실시하는 방법도 설명합니다.
목차
시작하며
경비 보고 샘플
통합 대조표
서비스 계약 정의
워크플로 런타임 호스트
배포 구성
서비스 사용
이중화 채널 구성
요약
상세 정보
사례
여기를 클릭하면, 샘플 응용 프로그램「Windows Workflow Foundation Sample Integrating WF
and WCF」(영어)을 다운로드할 수 있습니다.
처음에
마이크로소프트에서는 Windows Workflow Foundation (WF)를 이용한.NET 개발자 플랫폼에 워크플로 기능을
도입하였습니다. 개발자는 이기능을 다양한 시나리오에 적절한 워크플로를 구축할 수 있습니다. 단순한 순차적 워크플로에서
사용자와의 고도의 상호작용이 요구되는 복잡한 상태 머신 기반의 워크플로까지 다양합니다.
또한 캡슐화한 서비스의 끝점(Endpoint)을 사용해 비즈니스 기능을 공개 할 수
있으며, 비즈니스 기능과 프로세스 재이용이나
재편성이 가능하며, 서비스 지향 아키텍처의 실제 구현으로 이어집니다. WCF(Windows Communication Foundation) 는
배포를 지원하는 일관된 개발자 API, 견고한 호스팅 런타임 및 유연한 구성 주체의 솔루션을 제공하는 것으로, 연결된 시스템을 용이하게
개발할 수 있는 기능을 개발자에게 제공하도록 디자인되었습니다.
경비 보고 샘플
이 자료의 코드 샘플은 사원의 경비 청구와 그 승인이라고 하는 표준적인 비즈니스 프로세스를 모델화한 경비 보고 워크플로 샘플을
기본으로 합니다. 이 시나리오를 호스트 효과를 높이기 위해 어떻게 WCF 와 .NET Framework 3.0 을 사용하는지 보여주기
위해 원본 샘플을 수정하였습니다.
경비 보고 샘플의 첫 번째 출시 때는 클라이언트 응용 프로그램과 워크플로 런타임의 인스턴스를 포함한 호스트 응용 프로그램과의 통신을 .NET 리모팅을 사용합니다.
경비 보고의 구현을 리팩터링하고, WCF를 사용해 클라이언트와 서비스와의 통신을 실시하도록 했습니다. 이번 솔루션에서는 내부의
다양한 현안 사항이 각각 분리되도록 논리적인 구조화되어 있습니다.
.gif)
그림 1. 리팩터링된 솔루션의 구조
비즈니스 프로세스의 문맥 내에서, 메시지가 어떻게 사용되는지를 이해해 두는 것이 중요합니다.이것을 이해하는 것으로, 메시지를 디자인에
넣을 수 있습니다. 경비 보고의 라이프 사이클 안에는 상호작용의 포인트가 몇 가지 있습니다. 이러한 포인트를 간단하게 확인합시다.
- 프로세스에는 "클라이언트","상사" 및 경비 보고의 "호스트" 시스템이라고 하는 3 자가 관여합니다.
- 프로세스는 "클라이언트" 가 새롭게 경비를 청구하는 것으로 개시됩니다.
- 경비 청구를 자동적으로 승인할 수 있는지 판단할 때 규칙 "정책" 을 사용합니다.
- 경비 청구가 자동적으로 승인되지 않았던 경우, "상사" 가 보고의 승인을 실시할 필요가 있습니다. 상사는 승인이 필요한
신규 보고를 체크하는지, 승인 의뢰의 통지를 받을 필요가 있습니다.
- 일정한 시간 내에 상사가 승인하지 않으면 프로세스에서 그 청구가 자동적으로 거부됩니다.
- 경비 청구가 확인되면,"클라이언트" 와 "상사" 에서는 그 결과에 응한 갱신을 할 필요가 있습니다.
WF 를 사용하면, 프레임워크에 준비된 표준 액티비티를 사용하고, 이 프로세스를 모델화할 수 있습니다.
DelayActivity 를 사용하여 일정시간 경과후의 이벤트 트리거를 관리할 수 있습니다.또, 규칙 엔진과
PolicyActivity 를 사용하여 결과에 적용하는 유연한 규칙 세트를 관리할 수 있습니다.
사람이 관여된 프로세스이기 때문에 최종 사용자와의 상호작용을 통해, 그 상호작용의 결과를 워크플로에 반환해야 합니다. WF 에는HandleExternalEventActivity와 CallExternalMethodActivity 의 로컬 서비스가 이들 서비스에 의해, 호스트와 워크플로와의 통신을
가능한 경우 포괄적인 프로그래밍 모델이 제공됩니다.
이것은 상호작용 형태의 워크플로를 구축하는데 중요한 개념으로, WF 에서 어떻게 디자인 되었는지 간단히 설명합니다.
WF 로 상호작용을 모델화하려면, 다수의 이벤트와 메소드를 공개하는 계약(Contract)를 디자인해야 합니다.이
계약은 워크플로 프로세스와 호스트
프로세스의 양쪽 모두에 인식됩니다. 구축하는 계약과 인터페이스에는[ExternalDataExchange()] 속성을 붙이고,
워크플로 데이터의 교환을 위해서 디자인을 식별해야 합니다.샘플에서는 워크플로로 IExpenseLocalService
인터페이스를 사용합니다.
다음은 워크플로 런타임으로 이 인터페이스를 구현 하는 클래스 (로컬 서비스라고도 함)를 구독합니다. 워크플로의 액티비티는
이벤트에 등록하는지, 인터페이스형으로 정의된 메소드를 사용할 수 있어 등록을 마친 로컬 서비스에 연결시킬 수 있습니다. 제어의
반전 (Inversion of Control)이라는 패턴을 사용하여, 워크플로와 로컬 서비스의 구상형과의 긴밀한 관계를 없애고 있습니다.
이번 샘플에서는 ExpenseLocalService 클래스가 IExpenseLocalService 컨트랙트를 구현
합니다.
워크플로의 처음 실행시에 워크플로로 조작하는 초기 데이터 가방을 제공할 수 있습니다. 워크플로가 외부와의 상호작용이 필요한
포인트에 이르면, 워크플로 안에서 HandleExternalEventActivity 에 바인드 된 이벤트를 발생시킬 수
있습니다. 이 액티비티는 인터페이스형과 이벤트를 인수 받아, 이벤트가 발생하도록 실행을 계속할 수 있게 되면, 워크플로를 재개합니다.
워크플로에서 로컬 서비스에 콜백할 필요가 있는 경우는 CallExternalMethodActivity를 사용해,
인터페이스와 메소드명을 인수로서 건네주는 것으로 콜백 할 수 있습니다.
이러한 액티비티를 사용하면, 실행중의 워크플로와 양방향 통신을 호스트 프로세스 내에서 실행할 수 있습니다.또, WF 내에서 제어의
반전 패턴을 사용하는 것으로, 워크플로와 로컬 서비스간의 긴밀한 관계를 고려할 필요가 없습니다.
다만, 단순한 호스트 프로세스보다 광범위하게 되어, 다른 시스템이나 사용자 주도의 상호작용을 허가할 필요가 있습니다. 이 레벨의
상호작용은 상호작용 조작을 몇 개의 서비스에 분산하여, 다른 서비스나 사용자 구동의 응용 프로그램으로부터 이러한 서비스를 차례차례
호출하는 것으로 실현될 수 있습니다. WCF 는 이 메시징 기능을 유연한 방법으로 구축할 수 있는 프레임워크입니다.
WCF 와 통합하는 이번 시나리오는 다음과 같은 중요한 장점이 있습니다.
- 메시징을 조립하는 코드와 서비스의 구현을 떼어낼 수 있습니다.
- 시스템을 사이를 접속하는 코드가 적게 되어, 단순화됩니다.
- 유연하게 배포 할 수 있습니다.
- 호스트로부터 클라이언트에의 직접 콜백을 사용하고, 정보를 고속, 저부하로 갱신할 수 있습니다.
통합 대조표
WF 와 WCF 와의 통합을 실시하려면, 워크플로를 개시하거나 실행중의 워크플로와 상호작용 하거나 할 수 있는 많은 인터페이스
포인트를 소비자에게 제공하는 서비스 인터페이스를 공개할 필요가 있습니다. 서비스는 비즈니스 프로세스가 외부 엔터티 (프로세스에 관련된
사용자 등)와 상호작용 포인트에 대해 모델화합니다.
.gif)
그림 2. 경비 보고 시나리오 상호작용 포인트
이것을 실현하려면 , 다음의 작업을 실시할 필요가 있습니다.
- 서비스 계약를 정의한다.
- 이벤트를 사용해 새로운 워크플로를 작성하는 (또는 기존의 워크플로와 상호작용) 서비스 조작을 구현 한다.
- 서비스 호스트의 내부에서 워크플로 런타임의 인스턴스를 호스트 한다.
단순하게 워크플로를 호스트 하는 뿐만 아니라 WCF 이중화 채널을 사용하고, 워크플로에서 이벤트를 생성하여, 소비자가 되는
클라이언트에 돌려줄 수 있습니다. 경비 보고의 솔루션에서는 클라이언트가 데이터를 정기적으로 갱신하기 위해서 서비스를 폴링 하는 것에
의존하기 때문에), 이 방식이 유효합니다. 이외에 서비스로부터 직접 통지를 받을 수도 있습니다.
이 때문에, 다음의 작업을 실시할 필요가 있습니다.
- 콜백 계약를 정의한다.
- 이중화 채널을 지원 하는 바인딩을 사용한다.
서비스 계약(Service
Contract) 정의
WCF(Windows Communication Foundation) 에서는 서비스의 기능과 데이터의 교환을 추상적으로 정의하는
공식의 계약를 선언할 필요가 있습니다. 이것은 코드내에서 인터페이스를 선언하는 것에 의해서 정의합니다.
비즈니스 서비스를 디자인하고 있을 때, 일반적으로 요구 응답 형식의 협업 패턴을 사용합니다. 이 패턴을 사용할 때는 제공하는
계약로
다음의 세가지 측면을 채울 필요가 있습니다.
- 공개하는 조작. 서비스가 소비자에게 공개하는 기능으로, 인터페이스의 메소드입니다.
- 요구와 응답시구조화 된 데이터를 캡슐화하는 메시지. 각 메소드의 인수와 반환값의 형태입니다. WCF 용어에서는
일반적으로 메시지 계약라고 합니다.간단한 시나리오에서는 데이터 계약(Data Contract)이 됩니다.
- 서비스 경유로 교환할 수 있는 핵심이 되는 비즈니스 엔터티 데이터 정의. 메시지의 일부를 형성합니다. WCF
용어에서는 데이터 계약이라고 합니다.
서비스 계약는 속성 기반의 마크 업을 사용해 정의합니다.이 마크 업에서는 조작을 공개하는 계약와 네트워크 경유로 공개되는 특정의
조작을 정의합니다.
각 서비스 계약는 [ServiceContract] 속성으로 명시적으로 마크 올라갑니다. 이 속성의 선언에는 다음의 매개
변수를 사용할 수 있습니다.
- Name: WSDL 의 <portType> 요소로 선언되는 계약(Contract)의 이름을 제어합니다.
- Namespace: WSDL 의 <portType> 요소로 선언되는
계약(Contract)의 네임 스페이스를
제어합니다.
- SessionMode: 계약(Contract)에 세션을 지원 하는 바인드가 필요한 경우로 지정합니다.
- CallbackContract: 클라이언트 콜백에 사용하는 계약(Contract)를 지정합니다.
- ProtectionLevel: 계약(Contract)에 ProtectionLevel 속성을 지원 하는 바인드가
필요한 경우로 지정합니다.이 속성은 암호화와 디지털 서명의 요건을 선언하는데 사용합니다.
조작 선언
서비스는 공개하는 다수의 조작으로부터 구성됩니다. 조작은 [OperationContract] 속성으로 마크 올라가는
것으로, 계약(Contract)에 명시적으로 옵트인 합니다. ServiceContract 같이, OperationContract
에도 끝점(Endpoint)에 바인드 하는 방법을 제어하는 매개 변수가 다수 있습니다. 다음과 같은 매개 변수가 있습니다.
- Action: 조작을 일의에 식별하는 이름을 제어합니다. 끝점(Endpoint)로 메시지를 수신하면,
dispatch가 컨트롤과
액션을 사용하고, 호출하는 메소드를 결정합니다.
- IsOneWay: 조작은 요구 메시지를 받아들여, 응답을 작성하지 않는 것을 나타내 보입니다. 이것은 void
형태의 반환 값을 돌려주는 것으로는 다릅니다. void 형태의 반환 값을 돌려주는 경우는 결과 메시지가 생성됩니다.
- ProtectionLevel: 조작에 필요한 암호화 또는 서명의 요건을 지정합니다.
다음은 서비스 계약의 코드 예입니다.
-->
[ServiceContract]
public interface IExpenseService
{
[OperationContract]
GetExpenseReportsResponse GetExpenseReports();
[OperationContract]
GetExpenseReportResponse GetExpenseReport(GetExpenseReportRequest
getExpenseReportRequest);
}
메시지 엔터티와 데이터 엔터티 선언
메시지는 송신하는 메시지의 페이로드나 본문을 정의하는 클래스로서 모델화합니다. 이것은 ASP.NET
을 사용하는 웹 서비스를
구축할 때에 WS Contract First (WSCF)등의 도구를 사용해 메시지를 모델화하는 경우와 같습니다.
기본값으로 WCF 는 DataContractSerializer 라는 직렬화(serializer) 엔진을 사용하고, 데이터의 직렬화(serializer)
및 직렬화 해제 (XML에의 변환 및 XML 로부터의 변환)를 실시합니다.
System.Runtime.Serialization 네임 스페이스에의 참조를 추가하여 DataContractSerializer 를
사용합니다.다음은 클래스에 [DataContract] 속성을 붙여 공개 멤버에 [DataMember] 속성을
붙입니다.
[DataContract]
public class GetExpenseReportsResponse
{
private List<ExpenseReport> reports;
[DataMember]
public List<ExpenseReport> Reports
{
get { return reports; }
set { reports = value; }
}
}
메시지 안에서 사용되는 데이터 엔터티는 비즈니스 도메인내의 엔터티를 나타냅니다. 메시지 컨트랙트와 같이,
DataContractSerializer 를 사용해, 분산하는 것을 명시적으로 선택하는 멤버에 속성을 붙일 수 있습니다.또는 데이터의
모델화만을 실시하는 경우는 Public 필드의 접근 방식을 사용하고, 클래스를 직렬화(serializer) 가능하게 설정할 수 있습니다.
이번 샘플에서는 메시징의 마크 업에, 데이터 계약의 접근 방식을 사용했습니다. 실제의 시나리오에서는 보다 복잡한 스키마, 스키마로의
속성의 사용, SOAP 헤더를 사용하기 위한 요건 등을 취급하는 일이 자주 있습니다. WCF 에서는 본문 뿐만이 아니라 SOAP
Envelope 전체를 기술하는 클래스에 [MessageContract] 속성을 붙여 정의하는 기능을 제공하는
것으로, 이러한 취급이 어려운 시나리오의 요구를 채웁니다.
데이터 계약과 메시지 계약의 자세한 내용은 이 자료의 마지막에 있는 「상세
정보」에 기재한 MSDN 라이브러리의 각 자료를 참조해 주세요.
워크플로 런타임 호스트
서비스에서는 세션의 라이프 사이클 중에 서비스 형의 새로운 인스턴스를 작성 및 관리하는 경우, 일반적으로 동시 실행시의 행동을 고려에
넣을 필요가 있다. 이러한 상황으로 워크플로를 사용하는 경우, 서비스 호스트 인스턴스가 불려 갈 때마다가 아니고, 서비스 호스트
인스턴스의 라이프 사이클 중에, 워크플로 런타임의 인스턴스를 작성해 그 인스턴스를 관리할 필요가 있습니다.
이것을 실시하려면 , 서비스 호스트의 작성시에 액티브하게 되는 확장 클래스를 사용하는 것을 추천합니다. 이러한 확장 클래스에서는
워크플로 런타임의 글로벌 인스턴스가 작성 및 관리되어 독립한 각 서비스 인스턴스에서 글로벌 인스턴스에 접근 할 수 있습니다.
ServiceHost 에서 확장 클래스를 구현 하려면 ,IExtension<ServiceHostBase>
를 구현 하는 클래스를 작성합니다. 이번 솔루션에서는WcfExtensions 코드 프로젝트에 존재하는
WfWcfExtension 클래스에 이 예가 있습니다.
또, 두 개의 메소드를 구현 할 필요가 있습니다. 하나는 Attach 에서 확장 클래스가 그 새로운 오브젝트에 첨부 될
때 호출되면 다른 하나는 Detach 에서 새로운 오브젝트가 언로드되기 직전에 불려 갑니다.
Attach 메소드는 다음과 같이 필요한 서비스를 지정하여 WorkflowRuntime 의 새로운
인스턴스를 작성합니다. 이것을 workflowRuntime 로컬 Private 필드에 보관 합니다.
void IExtension<ServiceHostBase>.Attach(ServiceHostBase owner)
{
workflowRuntime = new WorkflowRuntime(workflowServicesConfig);
ExternalDataExchangeService exSvc = new ExternalDataExchangeService();
workflowRuntime.AddService(exSvc);
workflowRuntime.StartRuntime();
}
보이는 것처럼, 워크플로 런타임을 초기화할 때는 런타임의 개시 전에 서비스 인스턴스를 런타임에 추가할 필요도 있습니다. 솔루션의
빌드시는 일반적으로는 런타임을 개시하기 전에 서비스를 추가하는 것을 추천합니다. 다만, 결합이 관계된 경우는 지연 바인드 아프로치를
사용하는 것이 실용적인 경우가 있습니다.
이번 샘플에서는 WorkflowRuntime 개시 후, ExpenseService클래스의
SetUpWorkflowEnvironment 메소드의 일부로서ExpenseLocalService 의 인스턴스를
ExternalDataExchangeService에 추가합니다.
다음의 Detach 메소드는 StopRuntime 을 호출하여 런타임을 슛다운 합니다.
void IExtension<ServiceHostBase>.Detach(ServiceHostBase owner)
{
workflowRuntime.StopRuntime();
}
WorkflowRuntime 서비스 호스트의 실행의 일환으로서 작성 및 초기화되면, 서비스의 호출 전에
기존의 모든 워크플로를 속행할 수 있게 됩니다. 서비스 호스트가 종료할 경우에, 워크플로 런타임이 정상적으로 슛다운 됩니다.
주 워크플로의 호스트나 실행 시간의 긴 워크플로의 모델화를 실시할 때는 워크플로의 영속화 (SqlWorkflowPersistenceService
등)을 사용하는 것을 추천합니다. 응용 프로그램 또는 프로세스의 재시동을 처리하기 위한 상태의 영속화 메커니즘이
제공됩니다.
서비스 조작 작성
서비스의 동작을 포함한 클래스를 작성하려면, 서비스 계약를 정의하는 인터페이스를 하나 이상 구현 할 필요가 있습니다.
public class ExpenseService :
IExpenseService,
IExpenseServiceClient,
IExpenseServiceManager
이번은 워크플로에 통합하기 위해, 서비스 메소드에는 비즈니스 논리를 포함하지 않고, 비즈니스 프로세스를 캡슐화하는 실행중의
워크플로에 이벤트를 제어 또는 발생시키는 코드를 포함합니다.
워크플로를 처리하는 조작에서는 새로운 워크플로를 개시하던가 이미 실행중의 워크플로와 상호작용 합니다.
새로운 워크플로 인스턴스를 작성하려면, WorkflowRuntime을 사용하고, 필요한 종류의 워크플로의 새로운
인스턴스를 작성할 필요가 있습니다. ServiceHost 확장 클래스에는 하나를 이미 작성이 끝난 상태입니다. 이
인스턴스 참조를 취득하려면 ,OperationContext 를 사용해 사용자 지정 확장 클래스를 찾아낼 필요가 있습니다.
WfWcfExtension extension =
OperationContext.Current.Host.Extensions.Find<WfWcfExtension>();
workflowRuntime = extension.WorkflowRuntime;
OperationContext 는 서비스 메소드의 실행 문맥에의 접근을 제공하는 클래스입니다. 상기의 코드로 이해와
같이, 이 클래스에서는 현재의 서비스 메소드의 문맥을 제공하는 Current 싱글톤(Singleton)을 지정합니다.
Host 속성을 호출하고, 실행중의 ServiceHost 에 돌아오는 인스턴스를 패치하여, 그 형태를
기본으로 확장 클래스를 찾아냅니다.
확장 인스턴스에의 참조를 취득하면, 퍼블릭 속성에서 WorkflowRuntime을 돌려주고 그것을 사용해
SequentialWorkflow의 새로운 인스턴스를 작성할 수 있습니다.
Guid workflowInstanceId =
submitExpenseReportRequest.Report.ExpenseReportId;
Assembly asm = Assembly.Load("ExpenseWorkflows");
Type workflowType = asm.GetType("ExpenseWorkflows.SequentialWorkflow");
WorkflowInstance workflowInstance =
workflowRuntime.CreateWorkflow(workflowType, null, workflowInstanceId);
workflowInstance.Start();
expenseLocalService.RaiseExpenseReportSubmittedEvent(
workflowInstanceId, submitExpenseReportRequest.Report);
상기의 코드에서는 사전 정의된 형태를 기본으로 새로운 워크플로 인스턴스를 작성합니다. 이것은 형태의 인스턴스를 직접 작성하는 것도
실현될 수 있지만, 실제로는 엄밀하게 형태 지정된 바인드를 사용하는 것이 아니라, 실행시에 다이나믹 규칙에 근거해 워크플로를
작성하는 유연성이 있는 것을 알 수 있습니다.
마지막 행은 워크플로 최초의 HandleExternalEventActivity 에서 처리되는 이벤트를
발생시켜, 워크플로가 개시되는 것을 통지합니다. ExpenseLocalService 클래스의 인스턴스 경유로 이벤트를
발생합니다. 이번 샘플에서는 새로운 워크플로를 개시하는지, 기존의 워크플로에 이벤트를 발생시켜, 워크플로와 상호작용 하기 위해,
ExpenseLocalService를 사용합니다. 이 클래스는 비즈니스 프로세스를 캡슐화하는 메커니즘으로서 사용합니다.
내부적으로는 WF 를 사용해 구현합니다.
.gif)
그림 3. HandleExternalEventActivity로 시작되는 워크플로
지금부터 취급하는 다른 종류의 상황에서는 기존의 워크플로에 콜백하고, 이벤트를 발생시킵니다. 이경우 워크플로 엔진에 대해
이벤트를 발생시킬 필요가 있습니다.그 결과, 기존의 워크플로가 그 이벤트를 받아 처리를 속행합니다.
경비 보고 플로우내에서 발생하는 예로서 상사의 승인이 필요한 경우가 있습니다. 워크플로에서는
RequestManagerApproval 에의 외부 메소드를 호출하고, 새로운 경비 보고를 승인 또는 거부할 필요가 있다는 것을
상사에게 통지합니다.
워크플로에는 생각할 수 있는 이벤트 중 한쪽이 발생할 때까지 처리를 블록 하는 ListenActivity 가 있습니다.
이 경우, 상사가 보고를 확인한 것을 보여주는 이벤트를 받았는지, DelayActivity 에 근거해 타임 아웃 합니다.
.gif)
그림 4. ManagerApproval 사용자 지정 액티비티 플로우
Guid workflowInstanceId =
submitReviewedExpenseReportRequest.Report.ExpenseReportId;
ExpenseReportReviewedEventArgs e =
new ExpenseReportReviewedEventArgs(workflowInstanceId, report, review);
if (ExpenseReportReviewed != null)
{
ExpenseReportReviewed(null, e);
}
상사가 "ManagerApplication" 를 사용하여 보고를 확인하면, 서비스의 호출이 ExpenseReportReviewed
이벤트를 발생시키는SubmitReviewedExpenseReport 메소드를 호출합니다..
워크플로로 HandleExternalEventActivity 에의 이벤트를 발생시킬 때, 이벤트를 루팅 할 수 있도록,
처리하는 워크플로 인스턴스의 GUID 를 파악해둬야 합니다.
각 이벤트는 EventArgs 를 따라 발생합니다.이것에 의해, 이벤트 모델 경유로 워크플로에 데이터를 되돌릴 수
있습니다. 이 경우 보고의 현재 상태라고 확인 액티비티의 문맥을 제공하는 데이터의 양쪽 모두를 건네줄 수 있습니다.
워크플로에서는 이벤트는 HandleExternalEventActivity 속성을 사용해 자동적으로 워크플로와 연결할 수
있습니다.
.gif)
그림 5. IExpenseLocalService 인터페이스에
HandleExternalEventActivity 를 연결시킨다
[ExternalDataExchange] 속성을 붙일 필요가 있는 인터페이스형을 지정한 후, 그 인터페이스상에서
HandleExternalEventActivity 가 구독 하는 이벤트를 지정합니다.
이벤트의 인수는 ExternalDataEventArgs 클래스로부터 파생할 필요가 있습니다. 즉, 최악의 상황에서도 각
이벤트에는 워크플로의InstanceId 등의 문맥이 포함됩니다. 다음에, 워크플로 런타임이 적절한 워크플로 인스턴스에의
이벤트의 루팅을 관리해, 워크플로를 속행합니다.
서비스 호스트
WCF 서비스를 호스트 하려면, ServiceHost 컨테이너 내에서 실행해야 합니다.
WCF 를 사용하고 호스팅을 실현하는 방법을 조사하려면 , 우선 이용 가능한 대안을 이해하고
있어야 합니다.
- 표준의 Windows 프로세스의 경우, ServiceHost 인스턴스를 수동으로 작성하여 열 수 있습니다.
- Microsoft 인터넷 정보제공 서비스 (IIS) 6.0 을 사용해 Web 끝점(Endpoint) 서비스를 호스트 하고
있는 경우, System.ServiceModel 네임 스페이스로 제공되는 사용자 지정 HttpHandler를
사용합니다.
- IIS 7 으로 호스트 하고 있는 경우, Windows 액티브화 서비스 (WAS)를 사용해 끝점(Endpoint)을 호스트 할 수
있습니다.
일반적으로 Web 서비스를 구축하는 경우, 인터넷 정보제공 서비스를 사용해 호스트 하는 방법을 선택합니다. demon로서 기능하는
단일의 인스턴스의 끝점(Endpoint)을 구축하는 경우, 일반적으로 Windows 서비스를 사용해 호스트 하는 방법을 선택합니다.
이번 예에서는 Windows 콘솔 프로그램 내에서 주요한 서비스 인스턴스를 호스트 합니다.이것은 Windows 서비스를 호스트 하는
방법을 닮아 있습니다.
서비스를 배포 하려면, ServiceHost 클래스의 인스턴스를 작성하고, 공개하는 서비스형 마다
끝점(Endpoint)을 열
필요가 있습니다.ServiceHost 는 생성자의 일부로서 많은 인수를 받습니다.다만, 주된 인수는 Type
인수 또는 ServiceContract 를 구현 하는 클래스 인스턴스입니다.
- PerCall 또는PerSession 의 인스턴스화를 사용하는 경우는 Type을
사용합니다.
- Single 인스턴스화를 사용하는 경우는 단일의 인스턴스를 사용합니다.
WCF 내에서의 인스턴스화와 동시 실행의 자세한 내용은 MSDN Library 로 「Sessions,
Instancing, and Concurrency」(영어)을 참조해 주세요.
호스트를 확립하면, 사용 가능한 끝점(Endpoint)을 특정해 끝점(Endpoint)을 열어 공개하기 위해 확립된 호스트에 의해, 사용 가능한 구성
(자세한 내용은 다음의「배포 구성」참조)을 분석하여 그 구성과 명시적으로
추가된 구성이 머지됩니다. 클라이언트로부터 호출을 받으면, 그 요구가 새로운 백그라운드 워커 스레드로 처리되어 메시지 위의 SOAP
계약(Contract)명과 액션으로 지정된 적절한 서비스 조작에 루팅 됩니다.
using (ServiceHost serviceHost = new ServiceHost(new ExpenseService()))
{
WfWcfExtension wfWcfExtension =
new WfWcfExtension("WorkflowRuntimeConfig");
serviceHost.Extensions.Add(wfWcfExtension);
serviceHost.Open();
// block the process at this point, for example Console.ReadLine();
serviceHost.Close();
}
ServiceHost 의 구성은 접속의 끝점(Endpoint)을 열기 전에 실시할 필요가 있습니다. 이 조작을 실시하려면 , 상기와
같이,.Open() 을 호출하기 전에 호스트 객체와 상호작용합니다. using scope를 사용하여 ServiceHost를 사용하기 전에 파기하고, 마지막에 명시적으로 Close() 를 호출하고, 액티브한 접속과 끝점(Endpoint)을 올바르게
슛다운 하는 것을 추천합니다.
배포 구성
WCF 에서는 XML 구성에 의한 끝점(Endpoint)의 구성을 가능하게 하는 것으로써, 배포 때의 현안 사항과 구현 때의 현안 사항을
떼어내는 메커니즘을 제공합니다. 이 메커니즘에 의해 관리자는 코드를 재개발할 필요 없이 서비스의 정책을 변경할 수 있습니다.
각 서비스는 하나 이상의 끝점(Endpoint)로 공개됩니다. 끝점(Endpoint)은 단순한 주소 지정 가능한 접속 포인트로, 클라이언트는 여기로부터
서비스를 사용할 수 있습니다. WCF 에서는 각 끝점(Endpoint)은 WCF 의 ABC로서 알려져 있는 세 가지 속성을 사용해 선언됩니다.
세가지 속성은 Address, Binding ,Contract 입니다.
Address: 이 끝점(Endpoint)의 주소 지정 가능한 장소. 일반적으로 이 장소가 서비스가 요구를 수신 대기하는 절대
주소를 지정하는 URI 가 됩니다.예를 들어,"http://myhost/myservice" 나 "net.tcp://myhost:400/myservice"
와 같습니다.
Binding: 서비스와 그 소비자 사이의 통신용 프로토콜을 지정하는 정책. Binding 에서는 사용하는 전송의
종류, 메시지의 encode 방법, 데이터의 직렬화(serializer) 방법등의 측면을 지정합니다. WCF 에는 가장 일반적인
시나리오를 지원 하는 "바로 사용할 수 있는" 바인드가 다수 있습니다.
Contract: 코드로의 인터페이스 경유의 정의에 따라 공개된 조작과 데이터.
서비스를 구성하려면, 서비스 선언 구성을 선언하여, 서비스의 임의의 수의 끝점(Endpoint)을 구성할 필요가 있습니다. 서비스가 다수의
계약(Contract)를 구현하는 경우, 서비스의 구성은 공개할 필요가 있는 끝점(Endpoint)의 수에도 영향을 줍니다.
다음은 구성의 예입니다.
<services>
<service name="ExpenseServices.ExpenseService">
<endpoint
address="http://localhost:8081/ExpenseService/Manager"
binding="wsHttpBinding"
contract="ExpenseContracts.IExpenseServiceManager" />
<endpoint
address="http://localhost:8081/ExpenseService/Client"
binding="wsDualHttpBinding"
contract="ExpenseContracts.IExpenseServiceClient" />
</service>
</services>
이 구성 예는 ExpenseServices.ExpenseService 형태의 서비스 구성을 선언하고 있습니다.
이 형태에 근거하는 새로운 ServiceHost 인스턴스를 작성할 경우에, 런타임이 이 구성을 찾아낼 수
있습니다. 바인드의 자세한 내용은 MSDN Library「WCF
Bindings」 (http://msdn2.microsoft.com/en-us/library/ms733027.aspx)(영어)를
참조해 주세요.
서비스 사용
WCF 서비스의 사용은 ChannelFactory 클래스를 사용하여 실시합니다. ChannelFactory
는 팩토리 패턴을 사용하여, 지정된 끝점(Endpoint)에 접속하는 서비스 계약의 프록시 인스턴스를 제공합니다. 메시지를
암호화하거나 끝점(Endpoint) 정보를 동적으로 특정하기 위한 보안 자격 정보나 증명서 등의 런타임 정보를 사용해 팩토리를 구성할 수
있습니다.
private IExpenseServiceManager CreateChannelExpenseServiceManager()
{
ChannelFactory<IExpenseServiceManager> factory = new
ChannelFactory<IExpenseServiceManager>("ExpenseServiceManager");
IExpenseServiceManager proxy = factory.CreateChannel();
return proxy;
}
우선 팩토리의 인스턴스를 작성합니다.이 인스턴스는 필요한 계약(Contract)의 인스턴스만을 돌려주는 것보다 정확한 팩토리를 구축할 수
있도록, 서비스 계약에 제네릭 인수를 사용합니다. 또, 끝점(Endpoint)에 사용되는 구성을 결정하는 인수도 지정합니다. 여기에서는"ExpenseServiceManager"
라고 하는 끝점(Endpoint) 구성을 사용하여, 응용 프로그램 구성 파일내의 구성을 참조합니다.
<system.serviceModel>
<client>
<endpoint name="ExpenseServiceManager"
address="http://localhost:8081/ExpenseService/Manager"
binding="wsHttpBinding"
contract="ExpenseContracts.IExpenseServiceManager" />
</client>
</system.serviceModel>
끝점(Endpoint) 정의는 호스트의 구성으로 선언된 정의와 정확하게 일치하는 것을 알 수 있습니다. 일반적으로 구성이 다른 것은 네트워크
구성에 의해 클라이언트와 서버간의 주소가 다른 경우, 사용자 지정 동작이 구현되는 경우만 가능합니다.
Windows SDK 를 설치 하고 있으면, 솔루션에 통합할 수 있는 프록시 클래스와 끝점(Endpoint) 구성의 작성을 자동화하는 도구 ("svcutil")가
발견됩니다. 이 도구를 사용하려면, 대상의 서비스가 WSDL 또는 WS-MetadataExchange 를 사용해 메타 데이터 설명을
공개해야 합니다.
이중화 채널 구성
여기까지는 소비자로부터 송신하여, 서비스로 응답을 돌려주는 메시지를 사용해, 요구 응답 형식의 협업 패턴을 사용하는 커뮤니케이션
플로우를 전제로 이야기를 했습니다. WCF 에서는 단방향 통신이나 양방향 이중 통신 등의 많은 메시지 플로우가 지원 됩니다. 어느
쪽으로든 메시지 교환을 개시할 수 있는 메시지 플로우를 취급하고 있는 경우는 이중화 채널 또는 양방향 채널을 사용할 필요가 있습니다.
어느 방향으로부터도 데이터를 송신할 수 있는 접속성의 높은 시스템에서는 이중화 채널이 매우 효과적으로 될 가능성이 있습니다. 이중화
채널이 도움이 되는 예에는 이벤트 처리로부터의 콜백을 제공하는 경우 등이 있습니다.
클라이언트 콜백 구현
WCF 에서는"CallbackContracts" 라고 하는 개념을 사용하고, 클라이언트 콜백을 구현 합니다. 공개하는
계약(Contract)에서는
클라이언트가 공개하는 조작을 정의하는 두 번째의 계약(Contract)를 지정할 수 있습니다. 여기서 정의하는 조작은 서비스로 실행되는 코드로부터
콜백 할 수
있습니다.
CallbackContract 를 선언하려면, 콜백의 호출 바탕으로 되는 서비스 계약의 일부로서 인터페이스형을
지정합니다.
[ServiceContract(CallbackContract =
typeof(IExpenseServiceClientCallback))]
netTcpBinding 이나 wsDualHttpBinding 등 이중화 채널을 지원 하는 바인드도 사용할
필요가 있습니다. TCP 경유의 이중화는 메시지 교환 전체로 확립 및 관리되는 양방향 접속에 의해서 실현됩니다. HTTP 경유의 이중화는
클라이언트 리스너의 콜백에 의해 실현됩니다. 클라이언트는 접속의 귀가 경로를 인식하지 못하고, 구성을 사용해 이중화를 엄밀하게 정의하기
위해, clientBaseAddress 를 선언하는 사용자 지정 바인드 구성을 사용할 수 있습니다.
<endpoint binding="wsDualHttpBinding"
bindingConfiguration="AlternativeClientCallback"/>
<bindings>
<wsDualHttpBinding>
<binding name="AlternativeClientCallback"
clientBaseAddress="http://localhost:8082/ExpenseService/ClientCallback"/>
</wsDualHttpBinding>
</bindings>
클라이언트 콜백 구현
콜백 계약의 구현은 서버 계약의 구현과 동일합니다. 정의가 완료된 인터페이스의 구현을 제공할 필요가 있습니다.
class CallbackHandler : IExpenseServiceClientCallback
{
public void ExpenseReportReviewed(
ExpenseReportReviewedRequest expenseReportReviewedRequest)
{
// 여기에 콜백에 응답하는 클라이언트 논리를 구현 합니다.
}
}
호스트가 콜백한는 CallbackHandler 클래스의 인스턴스를 보관 유지할 수 있도록 하려면, 접속의 이중성을
인식하는 방법으로 클라이언트 채널을 설치 할 필요가 있습니다.
우선, 이중화 채널을 지원 하는 바인드를 사용합니다. 다음은 접속을 서비스 끝점(Endpoint)에 초기화할 경우에, 서비스에 대한 이중화
접속을 작성하는 DuplexChannelFactory 이라는 ChannelFactory 서브
클래스화한 버전을 사용합니다.
private IExpenseServiceClient CreateChannelExpenseServiceClient()
{
InstanceContext context = new InstanceContext(new CallbackHandler());
DuplexChannelFactory<IExpenseServiceClient> factory =
new DuplexChannelFactory<IExpenseServiceClient>(context,
"ExpenseServiceClient");
IExpenseServiceClient proxy = factory.CreateChannel();
return proxy;
}
DuplexChannelFactory 를 사용하는 경우의 주된 차이는 CallbackHandler 클래스의
인스턴스를 초기화하여, 팩토리 생성자에게 건네주고, 콜백에 사용하는 문맥을 초기화하는 점입니다.
호스트 콜백 구현
호스트의 관점에서 IExpenseServiceClient 계약으로 정의한 콜백 채널 경유로 행해지는
클라이언트에 대한 콜백 참조를 얻을 수 있습니다.
[ServiceContract(CallbackContract =
typeof(IExpenseServiceClientCallback))]
public interface IExpenseServiceClient : IExpenseService
CallbackContract 속성에서는 호스트로부터 행해지는 콜백의 계약(Contract)를 정의하는 인터페이스를 선언합니다.
콜백을 실시하려면 다음과 같이 OperationContext.Current.GetCallbackChannel
을 호출하여, 콜백 계약(Contract) 참조를 취득합니다.
IExpenseServiceClientCallback callback =
OperationContext.Current.GetCallbackChannel
<IExpenseServiceClientCallback>();
callback.ExpenseReportReviewed(new
ExpenseReportReviewedRequest(e.Report));
콜백 채널 참조를 취득하면, 그 콜백을 정상적으로 호출할 수 있습니다.
요약
Windows Workflow Foundation 는 워크플로를 정의하기 위한 전반적인 프레임워크와 실행중의 워크플로를 호스트
해, 그 워크플로와 상호작용 할 수 있는 견고한 런타임 엔진을 제공합니다.
Windows Communication Foundation 는 연결된 시스템을 구축하기 위한 전반적인 프레임워크를 제공해,
커뮤니케이션을 하는 방법을 정의하기 위한 일관성이 있는 API 와 광범위한 기능 세트를 개발자에 제공합니다.
이것들 2 개의 프레임워크를 병용 하는 것으로, 사용자의 환경 내에서 분산 비즈니스 프로세스를 구축 및 배포하기 위한 , 유연하고
포괄적인 응용 프로그램 플랫폼을 제공할 수 있습니다. WF 에서는 비즈니스 논리와 비즈니스 프로세스의 모델화와 캡슐화가 가능합니다.한편,
WCF 에서는 시스템을 분산하는 수단을 사용자에 제공하는 메시징 인프라가 제공됩니다.
이하에, 서비스를 디자인할 때에 기억해 두는 가이드 라인의 일부를 나타냅니다.
- 영속화 서비스를 사용하고, 장시간 실행되는 워크플로의 요구를 채웁니다.
- 서비스 조작은 이벤트의 발생에 의해, 실행중의 워크플로와 상호작용 할 수 있습니다. 주의를 재촉할 때는 이벤트를 발생해,
외부 (외부 서비스나 외부의 사용자 등)와 상호작용 할 때는 이벤트에 응답하도록 워크플로를 디자인합니다.
- 워크플로는 서비스 호출과는 비동기에게 실행됩니다.따라서, 서비스로부터 데이터를 돌려주는 것을 생각하는 타이밍과 그 때의
데이터 상태를 적절히 디자인합니다. synchronous communication를 사용하는 경우는 워크플로의 실행 스케줄을
수동으로 설정할 수 있는 ManualWorkflowSchedulerService 클래스를 사용할 수 있습니다.
상세 정보
-
Sessions, Instancing, and Concurrency (영어)
-
WCF Bindings (영어)
-
Using Data Contracts (영어)
-
Using Message Contracts (영어)
-
.NET Framework 3.0 커뮤니티 사이트 (영어)
저자에 대해
Jeremy Boyd 는 Intergen 의 시니어 기술 컨설턴트입니다. Intergen는 뉴질랜드에 본사 둔 솔루션 회사로,
마이크로소프트 공인 골드 파트너입니다.또, 뉴질랜드 커뮤니티의 MSDN 지역 디렉터입니다. Jeremy 는 12
개월간 WF 와 WCF 에 근간하는 솔루션 구현을 지원하기 위해서 고객과 적극적으로 협업해 왔습니다. 자신의
Weblog (http://turtle.net.nz/blog) (영어)를 통해 개발자 학습을 지원하고 있습니다.