다음을 통해 공유


워크플로 서비스 내/외부로 트랜잭션 흐름

워크플로 서비스 및 클라이언트는 트랜잭션에 참여할 수 있습니다. 서비스 작업이 앰비언트 트랜잭션의 일부가 되려면 Receive 또 다른 작업을 TransactedReceiveScope 작업 내에 배치해야 합니다. Send 또는 SendReply 액티비티가 TransactedReceiveScope 내에서 수행한 모든 호출은 앰비언트 트랜잭션 내에서도 이루어집니다. 워크플로 클라이언트 애플리케이션은 TransactionScope 활동을 사용하여 앰비언트 트랜잭션을 만들고, 해당 앰비언트 트랜잭션을 사용하여 서비스 작업을 호출할 수 있습니다. 이 항목에서는 트랜잭션에 참여하는 워크플로 서비스 및 워크플로 클라이언트를 만드는 방법에 대해 설명합니다.

경고

트랜잭션 내에서 워크플로 서비스 인스턴스가 로드되고 워크플로에 Persist 활동이 포함된 경우, 워크플로 인스턴스는 트랜잭션이 시간 초과될 때까지 차단됩니다.

중요합니다

TransactedReceiveScope를 사용할 때마다 모든 수신을 TransactedReceiveScope 활동 내에 워크플로에 배치하는 것이 좋습니다.

중요합니다

사용 중인 TransactedReceiveScope에서 메시지가 잘못된 순서로 도착할 경우, 잘못된 순서로 도착한 첫 번째 메시지를 배달하려고 하면 워크플로가 중단됩니다. 워크플로가 유휴 상태일 때 워크플로가 항상 일관된 중지 지점에 있는지 확인해야 합니다. 이렇게 하면 워크플로가 중단될 경우 이전 지속성 지점에서 워크플로를 다시 시작할 수 있습니다.

공유 라이브러리 만들기

  1. 빈 Visual Studio 솔루션을 새로 만듭니다.

  2. 라는 Common새 클래스 라이브러리 프로젝트를 추가합니다. 다음 어셈블리에 대한 참조를 추가합니다.

    • System.Activities.dll

    • System.ServiceModel.dll

    • System.ServiceModel.Activities.dll

    • System.Transactions.dll

  3. 프로젝트 PrintTransactionInfo에 새 클래스를 Common로 추가합니다. 이 클래스는 NativeActivity에서 파생되고 Execute 메서드를 오버로드합니다.

    using System;  
    using System;  
    using System.Activities;  
    using System.Transactions;  
    
    namespace Common  
    {  
        public class PrintTransactionInfo : NativeActivity  
        {  
            protected override void Execute(NativeActivityContext context)  
            {  
                RuntimeTransactionHandle rth = context.Properties.Find(typeof(RuntimeTransactionHandle).FullName) as RuntimeTransactionHandle;  
    
                if (rth == null)  
                {  
                    Console.WriteLine("There is no ambient RuntimeTransactionHandle");  
                }  
    
                Transaction t = rth.GetCurrentTransaction(context);  
    
                if (t == null)  
                {  
                    Console.WriteLine("There is no ambient transaction");  
                }  
                else  
                {  
                    Console.WriteLine("Transaction: {0} is {1}", t.TransactionInformation.DistributedIdentifier, t.TransactionInformation.Status);  
                }  
            }  
        }  
    
    }  
    

    이 작업은 앰비언트 트랜잭션에 대한 정보를 표시하는 네이티브 작업이며 이 항목에서 사용되는 서비스 및 클라이언트 워크플로 모두에서 사용됩니다. 도구 상자공통 섹션에서 이 작업을 사용할 수 있도록 솔루션을 빌드합니다.

워크플로 서비스 구현

  1. 새 WCF 워크플로 서비스 WorkflowServiceCommon 프로젝트에 추가합니다. 이렇게 하려면 프로젝트를 마우스 오른쪽 단추로 클릭하고 Common추가, 새 항목..., 설치된 템플릿에서 워크플로를 선택하고 WCF 워크플로 서비스를 선택합니다.

    워크플로 서비스 추가

  2. 기본값 ReceiveRequestSendResponse 활동을 삭제합니다.

  3. WriteLine 활동을 Sequential Service 활동에 끌어 놓습니다. 다음 예제와 같이 텍스트 속성을 "Workflow Service starting ..." 설정합니다.

    ![순차 서비스 활동에 WriteLine 활동 추가](./media/flowing-transactions-into-and-out-of-workflow-services/add-writeline-sequential-service.jpg)

  4. TransactedReceiveScope 활동 이후에 WriteLine를 드래그하여 놓습니다. 이 활동은 TransactedReceiveScope도구 상자메시징 섹션에서 찾을 수 있습니다. 활동은 TransactedReceiveScope요청본문의 두 섹션으로 구성됩니다. 요청 섹션에는 활동이 포함됩니다Receive. 본문 섹션에는 메시지를 받은 후 트랜잭션 내에서 실행할 활동이 포함되어 있습니다.

    TransactedReceiveScope 작업 추가

  5. 활동을 TransactedReceiveScope 선택하고 변수 단추를 클릭합니다. 다음 변수를 추가합니다.

    TransactedReceiveScope에 변수 추가

    비고

    기본적으로 있는 데이터 변수를 삭제할 수 있습니다. 기존 핸들 변수를 사용할 수도 있습니다.

  6. Receive 활동의 요청 섹션 내에 TransactedReceiveScope 활동을 드래그 앤 드롭합니다. 다음과 같이 속성을 설정합니다.

    재산 가치
    인스턴스 생성 가능 참(true) 확인란에 체크하세요
    작전이름 스타트샘플
    서비스계약명 ITransactionSample

    워크플로는 다음과 같습니다.

    수신 활동 추가

  7. 작업에서 Receive 링크를 클릭하고 다음 설정을 지정합니다.

    수신 활동에 대한 메시지 설정

  8. Sequence 활동을 끌어서 TransactedReceiveScope의 본문 섹션에 놓습니다. 작업 환경 Sequence에서는 두 개의 WriteLine 활동을 끌어다 놓고, 다음 표와 같이 Text 속성을 설정합니다.

    활동 가치
    첫 번째 WriteLine "서비스: 수신 완료됨"
    두 번째 WriteLine "서비스: 수신 = " + requestMessage

    이제 워크플로는 다음과 같이 표시됩니다.

    WriteLine 활동을 추가한 후 시퀀스

  9. 두 번째 PrintTransactionInfo 활동 후 WriteLine 활동에서 TransactedReceiveScope 활동을 끌어서 놓습니다.

    PrintTransactionInfo를 추가한 후 시퀀스

  10. Assign 활동을 끌어서 놓은 후, PrintTransactionInfo 활동의 속성을 다음 표에 따라 변경합니다.

    재산 가치
    에게 답장 메시지
    가치 "서비스: 회신 보내기"
  11. WriteLine 활동 후 Assign 활동을 드래그하여 놓고, 해당 Text 속성을 "서비스: 회신 시작"으로 설정합니다.

    이제 워크플로는 다음과 같이 표시됩니다.

    Assign 및 WriteLine을 추가한 후

  12. 활동을 Receive 마우스 오른쪽 단추로 클릭하고 SendReply 만들기 를 선택하고 마지막 WriteLine 작업 뒤에 붙여넣습니다. 활동에서 SendReplyToReceive 링크를 클릭하고 다음 설정을 지정합니다.

    회신 메시지 설정

  13. WriteLine 활동 후 SendReplyToReceive 활동을 끌어서 놓고, Text 속성을 "서비스: 회신 전송됨"으로 설정합니다.

  14. 워크플로 아래쪽에서 활동을 끌어서 놓 WriteLine 고 해당 Text 속성을 "서비스: 워크플로 종료, Enter 키를 눌러 종료"로 설정합니다.

    완료된 서비스 워크플로는 다음과 같습니다.

    서비스 워크플로 완료

워크플로 클라이언트 구현

  1. 새 WCF 워크플로 애플리케이션인 WorkflowClientCommon 프로젝트에 추가합니다. 이렇게 하려면 프로젝트를 마우스 오른쪽 단추로 클릭하고 Common추가, 새 항목..., 설치된 템플릿에서 워크플로를 선택하고 작업을 선택합니다.

    활동 프로젝트 추가

  2. Sequence 활동을 디자인 화면으로 끌어서 놓습니다.

  3. 활동 내에서 Sequence 활동을 드래그 앤 드롭하고, WriteLine 속성을 Text로 설정합니다. 이제 워크플로는 다음과 같이 표시됩니다.

    WriteLine 작업 추가

  4. TransactionScope 활동 이후에 WriteLine 활동을 끌어다 놓으세요. TransactionScope 활동을 선택하고 변수 단추를 클릭하고 다음 변수를 추가합니다.

    TransactionScope에 변수 추가

  5. 활동 SequenceTransactionScope 활동의 본문에 끌어서 놓습니다.

  6. PrintTransactionInfo 내에 Sequence 활동을 끌어서 놓기

  7. WriteLine 활동 이후에 PrintTransactionInfo 활동을 끌어서 놓고, 해당 Text 속성을 "클라이언트: 보내기 시작"으로 설정합니다. 이제 워크플로는 다음과 같이 표시됩니다.

    클라이언트 추가: 보내기 작업 시작

  8. Send 활동 뒤에 Assign 활동을 끌어서 놓고 다음 속성을 설정합니다.

    재산 가치
    엔드포인트 구성 이름 워크플로우 서비스 엔드포인트
    작전이름 샘플 시작
    서비스계약명 ITransactionSample

    이제 워크플로는 다음과 같이 표시됩니다.

    보내기 작업 속성 설정

  9. 정의... 링크를 클릭하고 다음 설정을 만듭니다.

    활동 메시지 설정 보내기

  10. 활동 Send을 마우스 오른쪽 단추로 클릭하고 Create ReceiveReply를 선택합니다. ReceiveReply 활동 후에 Send 활동은 자동으로 배치됩니다.

  11. ReceiveReplyForSend 활동의 '정의...' 링크를 클릭하고, 다음 설정을 지정합니다.

    ReceiveForSend 메시지 설정 설정

  12. WriteLine 활동을 Send 활동과 ReceiveReply 활동 사이에 끌어다 놓고 해당 Text 속성을 "클라이언트: 보내기 완료"로 설정합니다.

  13. WriteLine 활동 뒤에 ReceiveReply 활동을 끌어다 놓고, 해당 Text 속성을 "클라이언트 쪽: 회신 수신 = " + replyMessage"로 설정합니다.

  14. PrintTransactionInfo 활동 이후에 WriteLine 활동을 끌어다 놓으세요.

  15. 워크플로의 끝에 활동을 WriteLine 끌어서 놓고 해당 Text 속성을 "클라이언트 워크플로 종료"로 설정합니다. 완료된 클라이언트 워크플로는 다음 다이어그램과 같습니다.

    완료된 클라이언트 워크플로

  16. 솔루션을 빌드합니다.

서비스 애플리케이션 만들기

  1. 솔루션에 호출 Service 된 새 콘솔 애플리케이션 프로젝트를 추가합니다. 다음 어셈블리에 대한 참조를 추가합니다.

    1. System.Activities.dll

    2. System.ServiceModel.dll

    3. System.ServiceModel.Activities.dll

  2. 생성된 Program.cs 파일 및 다음 코드를 엽니다.

          static void Main()  
          {  
              Console.WriteLine("Building the server.");  
              using (WorkflowServiceHost host = new WorkflowServiceHost(new DeclarativeServiceWorkflow(), new Uri("net.tcp://localhost:8000/TransactedReceiveService/Declarative")))  
              {
                  //Start the server  
                  host.Open();  
                  Console.WriteLine("Service started.");  
    
                  Console.WriteLine();  
                  Console.ReadLine();  
                  //Shutdown  
                  host.Close();  
              };
          }  
    
  3. 프로젝트에 다음 app.config 파일을 추가합니다.

    <?xml version="1.0" encoding="utf-8" ?>  
    <!-- Copyright © Microsoft Corporation.  All rights reserved. -->  
    <configuration>  
        <system.serviceModel>  
            <bindings>  
                <netTcpBinding>  
                    <binding transactionFlow="true" />  
                </netTcpBinding>  
            </bindings>  
        </system.serviceModel>  
    </configuration>  
    

클라이언트 애플리케이션 만들기

  1. 솔루션에 호출 Client 된 새 콘솔 애플리케이션 프로젝트를 추가합니다. System.Activities.dll참조를 추가합니다.

  2. program.cs 파일을 열고 다음 코드를 추가합니다.

    class Program  
    {  
    
        private static AutoResetEvent syncEvent = new AutoResetEvent(false);  
    
        static void Main(string[] args)  
        {  
            //Build client  
            Console.WriteLine("Building the client.");  
            WorkflowApplication client = new WorkflowApplication(new DeclarativeClientWorkflow());  
            client.Completed = Program.Completed;  
            client.Aborted = Program.Aborted;  
            client.OnUnhandledException = Program.OnUnhandledException;  
            //Wait for service to start  
            Console.WriteLine("Press ENTER once service is started.");  
            Console.ReadLine();  
    
            //Start the client
            Console.WriteLine("Starting the client.");  
            client.Run();  
            syncEvent.WaitOne();  
    
            //Sample complete  
            Console.WriteLine();  
            Console.WriteLine("Client complete. Press ENTER to exit.");  
            Console.ReadLine();  
        }  
    
        private static void Completed(WorkflowApplicationCompletedEventArgs e)  
        {  
            Program.syncEvent.Set();  
        }  
    
        private static void Aborted(WorkflowApplicationAbortedEventArgs e)  
        {  
            Console.WriteLine("Client Aborted: {0}", e.Reason);  
            Program.syncEvent.Set();  
        }  
    
        private static UnhandledExceptionAction OnUnhandledException(WorkflowApplicationUnhandledExceptionEventArgs e)  
        {  
            Console.WriteLine("Client had an unhandled exception: {0}", e.UnhandledException);  
            return UnhandledExceptionAction.Cancel;  
        }  
    }  
    

참고하십시오